activerecord 4.1.0 → 4.2.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +776 -1330
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +12 -8
- data/lib/active_record/association_relation.rb +4 -0
- data/lib/active_record/associations/alias_tracker.rb +14 -13
- data/lib/active_record/associations/association.rb +2 -2
- data/lib/active_record/associations/association_scope.rb +83 -43
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/builder/association.rb +15 -4
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +9 -6
- data/lib/active_record/associations/builder/has_many.rb +1 -1
- data/lib/active_record/associations/builder/has_one.rb +2 -2
- data/lib/active_record/associations/builder/singular_association.rb +8 -1
- data/lib/active_record/associations/collection_association.rb +66 -29
- data/lib/active_record/associations/collection_proxy.rb +22 -26
- data/lib/active_record/associations/has_many_association.rb +65 -18
- data/lib/active_record/associations/has_many_through_association.rb +55 -27
- data/lib/active_record/associations/has_one_association.rb +0 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +19 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +20 -12
- data/lib/active_record/associations/preloader/association.rb +34 -11
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/preloader.rb +49 -59
- data/lib/active_record/associations/singular_association.rb +25 -4
- data/lib/active_record/associations/through_association.rb +23 -14
- data/lib/active_record/associations.rb +171 -42
- data/lib/active_record/attribute.rb +149 -0
- data/lib/active_record/attribute_assignment.rb +18 -10
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
- data/lib/active_record/attribute_methods/dirty.rb +98 -44
- data/lib/active_record/attribute_methods/primary_key.rb +14 -8
- data/lib/active_record/attribute_methods/query.rb +1 -1
- data/lib/active_record/attribute_methods/read.rb +22 -59
- data/lib/active_record/attribute_methods/serialization.rb +37 -147
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +34 -28
- data/lib/active_record/attribute_methods/write.rb +14 -21
- data/lib/active_record/attribute_methods.rb +67 -94
- data/lib/active_record/attribute_set/builder.rb +86 -0
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attributes.rb +139 -0
- data/lib/active_record/autosave_association.rb +45 -38
- data/lib/active_record/base.rb +10 -20
- data/lib/active_record/callbacks.rb +7 -7
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +78 -52
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +38 -59
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -55
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +126 -54
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +198 -64
- data/lib/active_record/connection_adapters/abstract/transaction.rb +126 -114
- data/lib/active_record/connection_adapters/abstract_adapter.rb +154 -55
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +240 -135
- data/lib/active_record/connection_adapters/column.rb +28 -239
- data/lib/active_record/connection_adapters/connection_specification.rb +16 -25
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -22
- data/lib/active_record/connection_adapters/mysql_adapter.rb +65 -149
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -27
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -374
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +55 -135
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +127 -38
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +220 -466
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -61
- data/lib/active_record/connection_handling.rb +3 -3
- data/lib/active_record/core.rb +143 -32
- data/lib/active_record/counter_cache.rb +60 -7
- data/lib/active_record/enum.rb +10 -11
- data/lib/active_record/errors.rb +49 -27
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixtures.rb +56 -70
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/locking/optimistic.rb +35 -17
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +52 -49
- data/lib/active_record/model_schema.rb +49 -57
- data/lib/active_record/nested_attributes.rb +7 -7
- data/lib/active_record/null_relation.rb +19 -5
- data/lib/active_record/persistence.rb +50 -31
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +14 -11
- data/lib/active_record/railties/databases.rake +56 -54
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +286 -102
- data/lib/active_record/relation/batches.rb +0 -1
- data/lib/active_record/relation/calculations.rb +39 -31
- data/lib/active_record/relation/delegation.rb +2 -2
- data/lib/active_record/relation/finder_methods.rb +80 -36
- data/lib/active_record/relation/merger.rb +25 -30
- data/lib/active_record/relation/predicate_builder/array_handler.rb +31 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +11 -10
- data/lib/active_record/relation/query_methods.rb +141 -55
- data/lib/active_record/relation/spawn_methods.rb +3 -0
- data/lib/active_record/relation.rb +69 -30
- data/lib/active_record/result.rb +18 -7
- data/lib/active_record/sanitization.rb +12 -2
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +58 -26
- data/lib/active_record/schema_migration.rb +11 -0
- data/lib/active_record/scoping/default.rb +8 -7
- data/lib/active_record/scoping/named.rb +4 -0
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +95 -10
- data/lib/active_record/store.rb +19 -10
- data/lib/active_record/tasks/database_tasks.rb +73 -7
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -2
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +11 -9
- data/lib/active_record/transactions.rb +37 -21
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +30 -0
- data/lib/active_record/type/date.rb +46 -0
- data/lib/active_record/type/date_time.rb +43 -0
- data/lib/active_record/type/decimal.rb +40 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
- data/lib/active_record/type/integer.rb +55 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +56 -0
- data/lib/active_record/type/string.rb +36 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +101 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/validations/associated.rb +5 -3
- data/lib/active_record/validations/presence.rb +6 -4
- data/lib/active_record/validations/uniqueness.rb +11 -17
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record.rb +3 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +4 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb +6 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +65 -10
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'arel/visitors/bind_visitor'
|
2
|
+
require 'active_support/core_ext/string/strip'
|
2
3
|
|
3
4
|
module ActiveRecord
|
4
5
|
module ConnectionAdapters
|
@@ -6,18 +7,35 @@ module ActiveRecord
|
|
6
7
|
include Savepoints
|
7
8
|
|
8
9
|
class SchemaCreation < AbstractAdapter::SchemaCreation
|
9
|
-
|
10
10
|
def visit_AddColumn(o)
|
11
11
|
add_column_position!(super, column_options(o))
|
12
12
|
end
|
13
13
|
|
14
14
|
private
|
15
|
+
|
16
|
+
def visit_DropForeignKey(name)
|
17
|
+
"DROP FOREIGN KEY #{name}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def visit_TableDefinition(o)
|
21
|
+
name = o.name
|
22
|
+
create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE #{quote_table_name(name)} "
|
23
|
+
|
24
|
+
statements = o.columns.map { |c| accept c }
|
25
|
+
statements.concat(o.indexes.map { |column_name, options| index_in_create(name, column_name, options) })
|
26
|
+
|
27
|
+
create_sql << "(#{statements.join(', ')}) " if statements.present?
|
28
|
+
create_sql << "#{o.options}"
|
29
|
+
create_sql << " AS #{@conn.to_sql(o.as)}" if o.as
|
30
|
+
create_sql
|
31
|
+
end
|
32
|
+
|
15
33
|
def visit_ChangeColumnDefinition(o)
|
16
34
|
column = o.column
|
17
35
|
options = o.options
|
18
36
|
sql_type = type_to_sql(o.type, options[:limit], options[:precision], options[:scale])
|
19
37
|
change_column_sql = "CHANGE #{quote_column_name(column.name)} #{quote_column_name(options[:name])} #{sql_type}"
|
20
|
-
add_column_options!(change_column_sql, options)
|
38
|
+
add_column_options!(change_column_sql, options.merge(column: column))
|
21
39
|
add_column_position!(change_column_sql, options)
|
22
40
|
end
|
23
41
|
|
@@ -29,6 +47,11 @@ module ActiveRecord
|
|
29
47
|
end
|
30
48
|
sql
|
31
49
|
end
|
50
|
+
|
51
|
+
def index_in_create(table_name, column_name, options)
|
52
|
+
index_name, index_type, index_columns, index_options, index_algorithm, index_using = @conn.add_index_options(table_name, column_name, options)
|
53
|
+
"#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_options} #{index_algorithm}"
|
54
|
+
end
|
32
55
|
end
|
33
56
|
|
34
57
|
def schema_creation
|
@@ -38,29 +61,25 @@ module ActiveRecord
|
|
38
61
|
class Column < ConnectionAdapters::Column # :nodoc:
|
39
62
|
attr_reader :collation, :strict, :extra
|
40
63
|
|
41
|
-
def initialize(name, default, sql_type = nil, null = true, collation = nil, strict = false, extra = "")
|
64
|
+
def initialize(name, default, cast_type, sql_type = nil, null = true, collation = nil, strict = false, extra = "")
|
42
65
|
@strict = strict
|
43
66
|
@collation = collation
|
44
67
|
@extra = extra
|
45
|
-
super(name, default, sql_type, null)
|
68
|
+
super(name, default, cast_type, sql_type, null)
|
69
|
+
assert_valid_default(default)
|
70
|
+
extract_default
|
46
71
|
end
|
47
72
|
|
48
|
-
def extract_default
|
73
|
+
def extract_default
|
49
74
|
if blob_or_text_column?
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
|
54
|
-
end
|
55
|
-
elsif missing_default_forged_as_empty_string?(default)
|
56
|
-
nil
|
57
|
-
else
|
58
|
-
super
|
75
|
+
@default = null || strict ? nil : ''
|
76
|
+
elsif missing_default_forged_as_empty_string?(@default)
|
77
|
+
@default = nil
|
59
78
|
end
|
60
79
|
end
|
61
80
|
|
62
81
|
def has_default?
|
63
|
-
return false if blob_or_text_column? #
|
82
|
+
return false if blob_or_text_column? # MySQL forbids defaults on blob and text columns
|
64
83
|
super
|
65
84
|
end
|
66
85
|
|
@@ -68,53 +87,18 @@ module ActiveRecord
|
|
68
87
|
sql_type =~ /blob/i || type == :text
|
69
88
|
end
|
70
89
|
|
71
|
-
# Must return the relevant concrete adapter
|
72
|
-
def adapter
|
73
|
-
raise NotImplementedError
|
74
|
-
end
|
75
|
-
|
76
90
|
def case_sensitive?
|
77
91
|
collation && !collation.match(/_ci$/)
|
78
92
|
end
|
79
93
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
case field_type
|
86
|
-
when /enum/i, /set/i then :string
|
87
|
-
when /year/i then :integer
|
88
|
-
when /bit/i then :binary
|
89
|
-
else
|
90
|
-
super
|
91
|
-
end
|
94
|
+
def ==(other)
|
95
|
+
super &&
|
96
|
+
collation == other.collation &&
|
97
|
+
strict == other.strict &&
|
98
|
+
extra == other.extra
|
92
99
|
end
|
93
100
|
|
94
|
-
|
95
|
-
case sql_type
|
96
|
-
when /^enum\((.+)\)/i
|
97
|
-
$1.split(',').map{|enum| enum.strip.length - 2}.max
|
98
|
-
when /blob|text/i
|
99
|
-
case sql_type
|
100
|
-
when /tiny/i
|
101
|
-
255
|
102
|
-
when /medium/i
|
103
|
-
16777215
|
104
|
-
when /long/i
|
105
|
-
2147483647 # mysql only allows 2^31-1, not 2^32-1, somewhat inconsistently with the tiny/medium/normal cases
|
106
|
-
else
|
107
|
-
super # we could return 65535 here, but we leave it undecorated by default
|
108
|
-
end
|
109
|
-
when /^bigint/i; 8
|
110
|
-
when /^int/i; 4
|
111
|
-
when /^mediumint/i; 3
|
112
|
-
when /^smallint/i; 2
|
113
|
-
when /^tinyint/i; 1
|
114
|
-
else
|
115
|
-
super
|
116
|
-
end
|
117
|
-
end
|
101
|
+
private
|
118
102
|
|
119
103
|
# MySQL misreports NOT NULL column default when none is given.
|
120
104
|
# We can't detect this for columns which may have a legitimate ''
|
@@ -126,6 +110,16 @@ module ActiveRecord
|
|
126
110
|
def missing_default_forged_as_empty_string?(default)
|
127
111
|
type != :string && !null && default == ''
|
128
112
|
end
|
113
|
+
|
114
|
+
def assert_valid_default(default)
|
115
|
+
if blob_or_text_column? && default.present?
|
116
|
+
raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def attributes_for_hash
|
121
|
+
super + [collation, strict, extra]
|
122
|
+
end
|
129
123
|
end
|
130
124
|
|
131
125
|
##
|
@@ -155,7 +149,6 @@ module ActiveRecord
|
|
155
149
|
:float => { :name => "float" },
|
156
150
|
:decimal => { :name => "decimal" },
|
157
151
|
:datetime => { :name => "datetime" },
|
158
|
-
:timestamp => { :name => "datetime" },
|
159
152
|
:time => { :name => "time" },
|
160
153
|
:date => { :name => "date" },
|
161
154
|
:binary => { :name => "blob" },
|
@@ -165,28 +158,21 @@ module ActiveRecord
|
|
165
158
|
INDEX_TYPES = [:fulltext, :spatial]
|
166
159
|
INDEX_USINGS = [:btree, :hash]
|
167
160
|
|
168
|
-
class BindSubstitution < Arel::Visitors::MySQL # :nodoc:
|
169
|
-
include Arel::Visitors::BindVisitor
|
170
|
-
end
|
171
|
-
|
172
161
|
# FIXME: Make the first parameter more similar for the two adapters
|
173
162
|
def initialize(connection, logger, connection_options, config)
|
174
163
|
super(connection, logger)
|
175
164
|
@connection_options, @config = connection_options, config
|
176
165
|
@quoted_column_names, @quoted_table_names = {}, {}
|
177
166
|
|
167
|
+
@visitor = Arel::Visitors::MySQL.new self
|
168
|
+
|
178
169
|
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
|
179
170
|
@prepared_statements = true
|
180
|
-
@visitor = Arel::Visitors::MySQL.new self
|
181
171
|
else
|
182
|
-
@
|
172
|
+
@prepared_statements = false
|
183
173
|
end
|
184
174
|
end
|
185
175
|
|
186
|
-
def adapter_name #:nodoc:
|
187
|
-
self.class::ADAPTER_NAME
|
188
|
-
end
|
189
|
-
|
190
176
|
# Returns true, since this connection adapter supports migrations.
|
191
177
|
def supports_migrations?
|
192
178
|
true
|
@@ -206,17 +192,6 @@ module ActiveRecord
|
|
206
192
|
true
|
207
193
|
end
|
208
194
|
|
209
|
-
def type_cast(value, column)
|
210
|
-
case value
|
211
|
-
when TrueClass
|
212
|
-
1
|
213
|
-
when FalseClass
|
214
|
-
0
|
215
|
-
else
|
216
|
-
super
|
217
|
-
end
|
218
|
-
end
|
219
|
-
|
220
195
|
# MySQL 4 technically support transaction isolation, but it is affected by a bug
|
221
196
|
# where the transaction level gets persisted for the whole session:
|
222
197
|
#
|
@@ -225,6 +200,18 @@ module ActiveRecord
|
|
225
200
|
version[0] >= 5
|
226
201
|
end
|
227
202
|
|
203
|
+
def supports_indexes_in_create?
|
204
|
+
true
|
205
|
+
end
|
206
|
+
|
207
|
+
def supports_foreign_keys?
|
208
|
+
true
|
209
|
+
end
|
210
|
+
|
211
|
+
def supports_views?
|
212
|
+
version[0] >= 5
|
213
|
+
end
|
214
|
+
|
228
215
|
def native_database_types
|
229
216
|
NATIVE_DATABASE_TYPES
|
230
217
|
end
|
@@ -241,12 +228,11 @@ module ActiveRecord
|
|
241
228
|
raise NotImplementedError
|
242
229
|
end
|
243
230
|
|
244
|
-
|
245
|
-
|
246
|
-
Column.new(field, default, type, null, collation, extra)
|
231
|
+
def new_column(field, default, cast_type, sql_type = nil, null = true, collation = "", extra = "") # :nodoc:
|
232
|
+
Column.new(field, default, cast_type, sql_type, null, collation, strict_mode?, extra)
|
247
233
|
end
|
248
234
|
|
249
|
-
# Must return the
|
235
|
+
# Must return the MySQL error number from the exception, if the exception has an
|
250
236
|
# error number.
|
251
237
|
def error_number(exception) # :nodoc:
|
252
238
|
raise NotImplementedError
|
@@ -254,12 +240,9 @@ module ActiveRecord
|
|
254
240
|
|
255
241
|
# QUOTING ==================================================
|
256
242
|
|
257
|
-
def
|
258
|
-
if value.
|
259
|
-
|
260
|
-
"x'#{s}'"
|
261
|
-
elsif value.kind_of?(BigDecimal)
|
262
|
-
value.to_s("F")
|
243
|
+
def _quote(value) # :nodoc:
|
244
|
+
if value.is_a?(Type::Binary::Data)
|
245
|
+
"x'#{value.hex}'"
|
263
246
|
else
|
264
247
|
super
|
265
248
|
end
|
@@ -277,10 +260,18 @@ module ActiveRecord
|
|
277
260
|
QUOTED_TRUE
|
278
261
|
end
|
279
262
|
|
263
|
+
def unquoted_true
|
264
|
+
1
|
265
|
+
end
|
266
|
+
|
280
267
|
def quoted_false
|
281
268
|
QUOTED_FALSE
|
282
269
|
end
|
283
270
|
|
271
|
+
def unquoted_false
|
272
|
+
0
|
273
|
+
end
|
274
|
+
|
284
275
|
# REFERENTIAL INTEGRITY ====================================
|
285
276
|
|
286
277
|
def disable_referential_integrity #:nodoc:
|
@@ -294,7 +285,14 @@ module ActiveRecord
|
|
294
285
|
end
|
295
286
|
end
|
296
287
|
|
288
|
+
#--
|
297
289
|
# DATABASE STATEMENTS ======================================
|
290
|
+
#++
|
291
|
+
|
292
|
+
def clear_cache!
|
293
|
+
super
|
294
|
+
reload_type_map
|
295
|
+
end
|
298
296
|
|
299
297
|
# Executes the SQL statement in the context of this connection.
|
300
298
|
def execute(sql, name = nil)
|
@@ -404,8 +402,12 @@ module ActiveRecord
|
|
404
402
|
end
|
405
403
|
end
|
406
404
|
|
405
|
+
def truncate(table_name, name = nil)
|
406
|
+
execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
|
407
|
+
end
|
408
|
+
|
407
409
|
def table_exists?(name)
|
408
|
-
return false unless name
|
410
|
+
return false unless name.present?
|
409
411
|
return true if tables(nil, nil, name).any?
|
410
412
|
|
411
413
|
name = name.to_s
|
@@ -449,7 +451,9 @@ module ActiveRecord
|
|
449
451
|
execute_and_free(sql, 'SCHEMA') do |result|
|
450
452
|
each_hash(result).map do |field|
|
451
453
|
field_name = set_field_encoding(field[:Field])
|
452
|
-
|
454
|
+
sql_type = field[:Type]
|
455
|
+
cast_type = lookup_cast_type(sql_type)
|
456
|
+
new_column(field_name, field[:Default], cast_type, sql_type, field[:Null] == "YES", field[:Collation], field[:Extra])
|
453
457
|
end
|
454
458
|
end
|
455
459
|
end
|
@@ -459,7 +463,7 @@ module ActiveRecord
|
|
459
463
|
end
|
460
464
|
|
461
465
|
def bulk_change_table(table_name, operations) #:nodoc:
|
462
|
-
sqls = operations.
|
466
|
+
sqls = operations.flat_map do |command, args|
|
463
467
|
table, arguments = args.shift, args
|
464
468
|
method = :"#{command}_sql"
|
465
469
|
|
@@ -468,7 +472,7 @@ module ActiveRecord
|
|
468
472
|
else
|
469
473
|
raise "Unknown method called : #{method}(#{arguments.inspect})"
|
470
474
|
end
|
471
|
-
end.
|
475
|
+
end.join(", ")
|
472
476
|
|
473
477
|
execute("ALTER TABLE #{quote_table_name(table_name)} #{sqls}")
|
474
478
|
end
|
@@ -483,18 +487,18 @@ module ActiveRecord
|
|
483
487
|
end
|
484
488
|
|
485
489
|
def drop_table(table_name, options = {})
|
486
|
-
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE #{quote_table_name(table_name)}"
|
490
|
+
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
487
491
|
end
|
488
492
|
|
489
493
|
def rename_index(table_name, old_name, new_name)
|
490
|
-
if
|
494
|
+
if supports_rename_index?
|
491
495
|
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME INDEX #{quote_table_name(old_name)} TO #{quote_table_name(new_name)}"
|
492
496
|
else
|
493
497
|
super
|
494
498
|
end
|
495
499
|
end
|
496
500
|
|
497
|
-
def change_column_default(table_name, column_name, default)
|
501
|
+
def change_column_default(table_name, column_name, default) #:nodoc:
|
498
502
|
column = column_for(table_name, column_name)
|
499
503
|
change_column table_name, column_name, column.sql_type, :default => default
|
500
504
|
end
|
@@ -523,6 +527,34 @@ module ActiveRecord
|
|
523
527
|
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options} #{index_algorithm}"
|
524
528
|
end
|
525
529
|
|
530
|
+
def foreign_keys(table_name)
|
531
|
+
fk_info = select_all <<-SQL.strip_heredoc
|
532
|
+
SELECT fk.referenced_table_name as 'to_table'
|
533
|
+
,fk.referenced_column_name as 'primary_key'
|
534
|
+
,fk.column_name as 'column'
|
535
|
+
,fk.constraint_name as 'name'
|
536
|
+
FROM information_schema.key_column_usage fk
|
537
|
+
WHERE fk.referenced_column_name is not null
|
538
|
+
AND fk.table_schema = '#{@config[:database]}'
|
539
|
+
AND fk.table_name = '#{table_name}'
|
540
|
+
SQL
|
541
|
+
|
542
|
+
create_table_info = select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
|
543
|
+
|
544
|
+
fk_info.map do |row|
|
545
|
+
options = {
|
546
|
+
column: row['column'],
|
547
|
+
name: row['name'],
|
548
|
+
primary_key: row['primary_key']
|
549
|
+
}
|
550
|
+
|
551
|
+
options[:on_update] = extract_foreign_key_action(create_table_info, row['name'], "UPDATE")
|
552
|
+
options[:on_delete] = extract_foreign_key_action(create_table_info, row['name'], "DELETE")
|
553
|
+
|
554
|
+
ForeignKeyDefinition.new(table_name, row['to_table'], options)
|
555
|
+
end
|
556
|
+
end
|
557
|
+
|
526
558
|
# Maps logical Rails types to MySQL-specific data types.
|
527
559
|
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
|
528
560
|
case type.to_s
|
@@ -555,14 +587,6 @@ module ActiveRecord
|
|
555
587
|
end
|
556
588
|
end
|
557
589
|
|
558
|
-
def add_column_position!(sql, options)
|
559
|
-
if options[:first]
|
560
|
-
sql << " FIRST"
|
561
|
-
elsif options[:after]
|
562
|
-
sql << " AFTER #{quote_column_name(options[:after])}"
|
563
|
-
end
|
564
|
-
end
|
565
|
-
|
566
590
|
# SHOW VARIABLES LIKE 'name'
|
567
591
|
def show_variable(name)
|
568
592
|
variables = select_all("SHOW VARIABLES LIKE '#{name}'", 'SCHEMA')
|
@@ -588,10 +612,19 @@ module ActiveRecord
|
|
588
612
|
pk_and_sequence && pk_and_sequence.first
|
589
613
|
end
|
590
614
|
|
591
|
-
def case_sensitive_modifier(node)
|
615
|
+
def case_sensitive_modifier(node, table_attribute)
|
616
|
+
node = Arel::Nodes.build_quoted node, table_attribute
|
592
617
|
Arel::Nodes::Bin.new(node)
|
593
618
|
end
|
594
619
|
|
620
|
+
def case_sensitive_comparison(table, attribute, column, value)
|
621
|
+
if column.case_sensitive?
|
622
|
+
table[attribute].eq(value)
|
623
|
+
else
|
624
|
+
super
|
625
|
+
end
|
626
|
+
end
|
627
|
+
|
595
628
|
def case_insensitive_comparison(table, attribute, column, value)
|
596
629
|
if column.case_sensitive?
|
597
630
|
super
|
@@ -600,10 +633,6 @@ module ActiveRecord
|
|
600
633
|
end
|
601
634
|
end
|
602
635
|
|
603
|
-
def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
|
604
|
-
where_sql
|
605
|
-
end
|
606
|
-
|
607
636
|
def strict_mode?
|
608
637
|
self.class.type_cast_config_to_boolean(@config.fetch(:strict, true))
|
609
638
|
end
|
@@ -614,6 +643,50 @@ module ActiveRecord
|
|
614
643
|
|
615
644
|
protected
|
616
645
|
|
646
|
+
def initialize_type_map(m) # :nodoc:
|
647
|
+
super
|
648
|
+
|
649
|
+
register_class_with_limit m, %r(char)i, MysqlString
|
650
|
+
|
651
|
+
m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
|
652
|
+
m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
|
653
|
+
m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
|
654
|
+
m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
|
655
|
+
m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
|
656
|
+
m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
|
657
|
+
m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
|
658
|
+
m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
|
659
|
+
m.register_type %r(^float)i, Type::Float.new(limit: 24)
|
660
|
+
m.register_type %r(^double)i, Type::Float.new(limit: 53)
|
661
|
+
|
662
|
+
register_integer_type m, %r(^bigint)i, limit: 8
|
663
|
+
register_integer_type m, %r(^int)i, limit: 4
|
664
|
+
register_integer_type m, %r(^mediumint)i, limit: 3
|
665
|
+
register_integer_type m, %r(^smallint)i, limit: 2
|
666
|
+
register_integer_type m, %r(^tinyint)i, limit: 1
|
667
|
+
|
668
|
+
m.alias_type %r(tinyint\(1\))i, 'boolean' if emulate_booleans
|
669
|
+
m.alias_type %r(set)i, 'varchar'
|
670
|
+
m.alias_type %r(year)i, 'integer'
|
671
|
+
m.alias_type %r(bit)i, 'binary'
|
672
|
+
|
673
|
+
m.register_type(%r(enum)i) do |sql_type|
|
674
|
+
limit = sql_type[/^enum\((.+)\)/i, 1]
|
675
|
+
.split(',').map{|enum| enum.strip.length - 2}.max
|
676
|
+
MysqlString.new(limit: limit)
|
677
|
+
end
|
678
|
+
end
|
679
|
+
|
680
|
+
def register_integer_type(mapping, key, options) # :nodoc:
|
681
|
+
mapping.register_type(key) do |sql_type|
|
682
|
+
if /unsigned/i =~ sql_type
|
683
|
+
Type::UnsignedInteger.new(options)
|
684
|
+
else
|
685
|
+
Type::Integer.new(options)
|
686
|
+
end
|
687
|
+
end
|
688
|
+
end
|
689
|
+
|
617
690
|
# MySQL is too stupid to create a temporary table for use subquery, so we have
|
618
691
|
# to give it some prompting in the form of a subsubquery. Ugh!
|
619
692
|
def subquery_for(key, select)
|
@@ -683,15 +756,13 @@ module ActiveRecord
|
|
683
756
|
end
|
684
757
|
|
685
758
|
def rename_column_sql(table_name, column_name, new_column_name)
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
|
694
|
-
end
|
759
|
+
column = column_for(table_name, column_name)
|
760
|
+
options = {
|
761
|
+
name: new_column_name,
|
762
|
+
default: column.default,
|
763
|
+
null: column.null,
|
764
|
+
auto_increment: column.extra == "auto_increment"
|
765
|
+
}
|
695
766
|
|
696
767
|
current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'", 'SCHEMA')["Type"]
|
697
768
|
schema_creation.accept ChangeColumnDefinition.new column, current_type, options
|
@@ -715,57 +786,62 @@ module ActiveRecord
|
|
715
786
|
"DROP INDEX #{index_name}"
|
716
787
|
end
|
717
788
|
|
718
|
-
def add_timestamps_sql(table_name)
|
719
|
-
[add_column_sql(table_name, :created_at, :datetime), add_column_sql(table_name, :updated_at, :datetime)]
|
789
|
+
def add_timestamps_sql(table_name, options = {})
|
790
|
+
[add_column_sql(table_name, :created_at, :datetime, options), add_column_sql(table_name, :updated_at, :datetime, options)]
|
720
791
|
end
|
721
792
|
|
722
|
-
def remove_timestamps_sql(table_name)
|
793
|
+
def remove_timestamps_sql(table_name, options = {})
|
723
794
|
[remove_column_sql(table_name, :updated_at), remove_column_sql(table_name, :created_at)]
|
724
795
|
end
|
725
796
|
|
726
797
|
private
|
727
798
|
|
728
|
-
def
|
729
|
-
version
|
799
|
+
def version
|
800
|
+
@version ||= full_version.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
|
730
801
|
end
|
731
802
|
|
732
|
-
def
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
803
|
+
def mariadb?
|
804
|
+
full_version =~ /mariadb/i
|
805
|
+
end
|
806
|
+
|
807
|
+
def supports_rename_index?
|
808
|
+
mariadb? ? false : (version[0] == 5 && version[1] >= 7) || version[0] >= 6
|
737
809
|
end
|
738
810
|
|
739
811
|
def configure_connection
|
740
|
-
variables = @config
|
812
|
+
variables = @config.fetch(:variables, {}).stringify_keys
|
741
813
|
|
742
814
|
# By default, MySQL 'where id is null' selects the last inserted id.
|
743
815
|
# Turn this off. http://dev.rubyonrails.org/ticket/6778
|
744
|
-
variables[
|
816
|
+
variables['sql_auto_is_null'] = 0
|
745
817
|
|
746
818
|
# Increase timeout so the server doesn't disconnect us.
|
747
819
|
wait_timeout = @config[:wait_timeout]
|
748
820
|
wait_timeout = 2147483 unless wait_timeout.is_a?(Fixnum)
|
749
|
-
variables[
|
821
|
+
variables['wait_timeout'] = self.class.type_cast_config_to_integer(wait_timeout)
|
750
822
|
|
751
823
|
# Make MySQL reject illegal values rather than truncating or blanking them, see
|
752
824
|
# http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_strict_all_tables
|
753
825
|
# If the user has provided another value for sql_mode, don't replace it.
|
754
|
-
|
755
|
-
variables[
|
826
|
+
unless variables.has_key?('sql_mode')
|
827
|
+
variables['sql_mode'] = strict_mode? ? 'STRICT_ALL_TABLES' : ''
|
756
828
|
end
|
757
829
|
|
758
830
|
# NAMES does not have an equals sign, see
|
759
831
|
# http://dev.mysql.com/doc/refman/5.0/en/set-statement.html#id944430
|
760
832
|
# (trailing comma because variable_assignments will always have content)
|
761
|
-
|
833
|
+
if @config[:encoding]
|
834
|
+
encoding = "NAMES #{@config[:encoding]}"
|
835
|
+
encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
|
836
|
+
encoding << ", "
|
837
|
+
end
|
762
838
|
|
763
839
|
# Gather up all of the SET variables...
|
764
840
|
variable_assignments = variables.map do |k, v|
|
765
841
|
if v == ':default' || v == :default
|
766
|
-
"@@SESSION.#{k
|
842
|
+
"@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
|
767
843
|
elsif !v.nil?
|
768
|
-
"@@SESSION.#{k
|
844
|
+
"@@SESSION.#{k} = #{quote(v)}"
|
769
845
|
end
|
770
846
|
# or else nil; compact to clear nils out
|
771
847
|
end.compact.join(', ')
|
@@ -773,6 +849,35 @@ module ActiveRecord
|
|
773
849
|
# ...and send them all in one query
|
774
850
|
@connection.query "SET #{encoding} #{variable_assignments}"
|
775
851
|
end
|
852
|
+
|
853
|
+
def extract_foreign_key_action(structure, name, action) # :nodoc:
|
854
|
+
if structure =~ /CONSTRAINT #{quote_column_name(name)} FOREIGN KEY .* REFERENCES .* ON #{action} (CASCADE|SET NULL|RESTRICT)/
|
855
|
+
case $1
|
856
|
+
when 'CASCADE'; :cascade
|
857
|
+
when 'SET NULL'; :nullify
|
858
|
+
end
|
859
|
+
end
|
860
|
+
end
|
861
|
+
|
862
|
+
class MysqlString < Type::String # :nodoc:
|
863
|
+
def type_cast_for_database(value)
|
864
|
+
case value
|
865
|
+
when true then "1"
|
866
|
+
when false then "0"
|
867
|
+
else super
|
868
|
+
end
|
869
|
+
end
|
870
|
+
|
871
|
+
private
|
872
|
+
|
873
|
+
def cast_value(value)
|
874
|
+
case value
|
875
|
+
when true then "1"
|
876
|
+
when false then "0"
|
877
|
+
else super
|
878
|
+
end
|
879
|
+
end
|
880
|
+
end
|
776
881
|
end
|
777
882
|
end
|
778
883
|
end
|