activerecord 3.1.10 → 4.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +6 -6
- data/CHANGELOG.md +1837 -338
- data/MIT-LICENSE +1 -1
- data/README.rdoc +39 -43
- data/examples/performance.rb +51 -20
- data/examples/simple.rb +4 -4
- data/lib/active_record/aggregations.rb +57 -43
- data/lib/active_record/association_relation.rb +35 -0
- data/lib/active_record/associations/alias_tracker.rb +47 -39
- data/lib/active_record/associations/association.rb +71 -85
- data/lib/active_record/associations/association_scope.rb +138 -89
- data/lib/active_record/associations/belongs_to_association.rb +65 -25
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +9 -3
- data/lib/active_record/associations/builder/association.rb +125 -29
- data/lib/active_record/associations/builder/belongs_to.rb +91 -60
- data/lib/active_record/associations/builder/collection_association.rb +69 -49
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +113 -42
- data/lib/active_record/associations/builder/has_many.rb +8 -64
- data/lib/active_record/associations/builder/has_one.rb +12 -52
- data/lib/active_record/associations/builder/singular_association.rb +22 -29
- data/lib/active_record/associations/collection_association.rb +294 -187
- data/lib/active_record/associations/collection_proxy.rb +961 -94
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +118 -23
- data/lib/active_record/associations/has_many_through_association.rb +115 -45
- data/lib/active_record/associations/has_one_association.rb +57 -24
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +76 -102
- data/lib/active_record/associations/join_dependency/join_base.rb +7 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +30 -37
- data/lib/active_record/associations/join_dependency.rb +230 -156
- data/lib/active_record/associations/preloader/association.rb +96 -55
- data/lib/active_record/associations/preloader/collection_association.rb +3 -3
- data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
- data/lib/active_record/associations/preloader/has_one.rb +1 -1
- data/lib/active_record/associations/preloader/singular_association.rb +3 -3
- data/lib/active_record/associations/preloader/through_association.rb +61 -32
- data/lib/active_record/associations/preloader.rb +113 -87
- data/lib/active_record/associations/singular_association.rb +29 -13
- data/lib/active_record/associations/through_association.rb +37 -19
- data/lib/active_record/associations.rb +505 -371
- data/lib/active_record/attribute.rb +163 -0
- data/lib/active_record/attribute_assignment.rb +212 -0
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
- data/lib/active_record/attribute_methods/dirty.rb +141 -51
- data/lib/active_record/attribute_methods/primary_key.rb +87 -36
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +74 -117
- data/lib/active_record/attribute_methods/serialization.rb +70 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +49 -47
- data/lib/active_record/attribute_methods/write.rb +60 -21
- data/lib/active_record/attribute_methods.rb +409 -48
- data/lib/active_record/attribute_set/builder.rb +106 -0
- data/lib/active_record/attribute_set.rb +81 -0
- data/lib/active_record/attributes.rb +147 -0
- data/lib/active_record/autosave_association.rb +279 -232
- data/lib/active_record/base.rb +84 -1969
- data/lib/active_record/callbacks.rb +66 -28
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/coders/yaml_column.rb +18 -21
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +422 -243
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +170 -194
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +32 -19
- data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -57
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +273 -170
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +731 -254
- data/lib/active_record/connection_adapters/abstract/transaction.rb +215 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +339 -95
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +946 -0
- data/lib/active_record/connection_adapters/column.rb +33 -221
- data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +140 -602
- data/lib/active_record/connection_adapters/mysql_adapter.rb +254 -756
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -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 +15 -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 +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -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 +19 -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 +109 -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 +36 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +596 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +445 -902
- data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +578 -25
- data/lib/active_record/connection_handling.rb +132 -0
- data/lib/active_record/core.rb +579 -0
- data/lib/active_record/counter_cache.rb +159 -102
- data/lib/active_record/dynamic_matchers.rb +140 -0
- data/lib/active_record/enum.rb +197 -0
- data/lib/active_record/errors.rb +102 -34
- data/lib/active_record/explain.rb +38 -0
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +29 -0
- data/lib/active_record/fixture_set/file.rb +56 -0
- data/lib/active_record/fixtures.rb +318 -260
- data/lib/active_record/gem_version.rb +15 -0
- data/lib/active_record/inheritance.rb +247 -0
- data/lib/active_record/integration.rb +113 -0
- data/lib/active_record/legacy_yaml_adapter.rb +30 -0
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +80 -52
- data/lib/active_record/locking/pessimistic.rb +27 -5
- data/lib/active_record/log_subscriber.rb +25 -18
- data/lib/active_record/migration/command_recorder.rb +130 -38
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/migration.rb +532 -201
- data/lib/active_record/model_schema.rb +342 -0
- data/lib/active_record/nested_attributes.rb +229 -139
- data/lib/active_record/no_touching.rb +52 -0
- data/lib/active_record/null_relation.rb +81 -0
- data/lib/active_record/persistence.rb +304 -99
- data/lib/active_record/query_cache.rb +25 -43
- data/lib/active_record/querying.rb +68 -0
- data/lib/active_record/railtie.rb +86 -45
- data/lib/active_record/railties/console_sandbox.rb +3 -4
- data/lib/active_record/railties/controller_runtime.rb +7 -4
- data/lib/active_record/railties/databases.rake +198 -377
- data/lib/active_record/railties/jdbcmysql_error.rb +2 -2
- data/lib/active_record/readonly_attributes.rb +23 -0
- data/lib/active_record/reflection.rb +516 -165
- data/lib/active_record/relation/batches.rb +96 -45
- data/lib/active_record/relation/calculations.rb +221 -144
- data/lib/active_record/relation/delegation.rb +140 -0
- data/lib/active_record/relation/finder_methods.rb +362 -243
- data/lib/active_record/relation/merger.rb +193 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
- data/lib/active_record/relation/predicate_builder.rb +135 -41
- data/lib/active_record/relation/query_methods.rb +982 -155
- data/lib/active_record/relation/spawn_methods.rb +50 -110
- data/lib/active_record/relation.rb +371 -180
- data/lib/active_record/result.rb +109 -12
- data/lib/active_record/runtime_registry.rb +22 -0
- data/lib/active_record/sanitization.rb +191 -0
- data/lib/active_record/schema.rb +19 -13
- data/lib/active_record/schema_dumper.rb +111 -61
- data/lib/active_record/schema_migration.rb +53 -0
- data/lib/active_record/scoping/default.rb +135 -0
- data/lib/active_record/scoping/named.rb +164 -0
- data/lib/active_record/scoping.rb +87 -0
- data/lib/active_record/serialization.rb +7 -45
- data/lib/active_record/serializers/xml_serializer.rb +14 -65
- data/lib/active_record/statement_cache.rb +111 -0
- data/lib/active_record/store.rb +205 -0
- data/lib/active_record/tasks/database_tasks.rb +299 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +159 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +101 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
- data/lib/active_record/timestamp.rb +35 -14
- data/lib/active_record/transactions.rb +141 -74
- data/lib/active_record/translation.rb +22 -0
- 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 +31 -0
- data/lib/active_record/type/date.rb +50 -0
- data/lib/active_record/type/date_time.rb +54 -0
- data/lib/active_record/type/decimal.rb +64 -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 +23 -0
- data/lib/active_record/type/integer.rb +59 -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 +62 -0
- data/lib/active_record/type/string.rb +40 -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 +110 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/validations/associated.rb +27 -18
- data/lib/active_record/validations/presence.rb +67 -0
- data/lib/active_record/validations/uniqueness.rb +125 -66
- data/lib/active_record/validations.rb +37 -30
- data/lib/active_record/version.rb +5 -7
- data/lib/active_record.rb +80 -25
- data/lib/rails/generators/active_record/migration/migration_generator.rb +54 -9
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +25 -11
- data/lib/rails/generators/active_record/migration.rb +11 -8
- data/lib/rails/generators/active_record/model/model_generator.rb +17 -4
- data/lib/rails/generators/active_record/model/templates/model.rb +5 -2
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- data/lib/rails/generators/active_record.rb +3 -11
- metadata +132 -53
- data/examples/associations.png +0 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -62
- data/lib/active_record/associations/join_helper.rb +0 -55
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -135
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -556
- data/lib/active_record/dynamic_finder_match.rb +0 -56
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/identity_map.rb +0 -163
- data/lib/active_record/named_scope.rb +0 -200
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/session_store.rb +0 -358
- data/lib/active_record/test_case.rb +0 -69
- data/lib/rails/generators/active_record/model/templates/migration.rb +0 -17
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -16
@@ -1,17 +1,12 @@
|
|
1
|
-
require 'active_record/connection_adapters/
|
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 '
|
3
|
+
require 'active_support/core_ext/hash/keys'
|
7
4
|
|
8
|
-
gem 'mysql', '~> 2.
|
5
|
+
gem 'mysql', '~> 2.9'
|
9
6
|
require 'mysql'
|
10
7
|
|
11
8
|
class Mysql
|
12
9
|
class Time
|
13
|
-
###
|
14
|
-
# This monkey patch is for test_additional_columns_from_join_table
|
15
10
|
def to_date
|
16
11
|
Date.new(year, month, day)
|
17
12
|
end
|
@@ -21,9 +16,9 @@ class Mysql
|
|
21
16
|
end
|
22
17
|
|
23
18
|
module ActiveRecord
|
24
|
-
|
19
|
+
module ConnectionHandling # :nodoc:
|
25
20
|
# Establishes a connection to the database that's used by all Active Record objects.
|
26
|
-
def
|
21
|
+
def mysql_connection(config)
|
27
22
|
config = config.symbolize_keys
|
28
23
|
host = config[:host]
|
29
24
|
port = config[:port]
|
@@ -39,96 +34,16 @@ module ActiveRecord
|
|
39
34
|
default_flags |= Mysql::CLIENT_FOUND_ROWS if Mysql.const_defined?(:CLIENT_FOUND_ROWS)
|
40
35
|
options = [host, username, password, database, port, socket, default_flags]
|
41
36
|
ConnectionAdapters::MysqlAdapter.new(mysql, logger, options, config)
|
37
|
+
rescue Mysql::Error => error
|
38
|
+
if error.message.include?("Unknown database")
|
39
|
+
raise ActiveRecord::NoDatabaseError.new(error.message, error)
|
40
|
+
else
|
41
|
+
raise
|
42
|
+
end
|
42
43
|
end
|
43
44
|
end
|
44
45
|
|
45
46
|
module ConnectionAdapters
|
46
|
-
class MysqlColumn < Column #:nodoc:
|
47
|
-
class << self
|
48
|
-
def string_to_time(value)
|
49
|
-
return super unless Mysql::Time === value
|
50
|
-
new_time(
|
51
|
-
value.year,
|
52
|
-
value.month,
|
53
|
-
value.day,
|
54
|
-
value.hour,
|
55
|
-
value.minute,
|
56
|
-
value.second,
|
57
|
-
value.second_part)
|
58
|
-
end
|
59
|
-
|
60
|
-
def string_to_dummy_time(v)
|
61
|
-
return super unless Mysql::Time === v
|
62
|
-
new_time(2000, 01, 01, v.hour, v.minute, v.second, v.second_part)
|
63
|
-
end
|
64
|
-
|
65
|
-
def string_to_date(v)
|
66
|
-
return super unless Mysql::Time === v
|
67
|
-
new_date(v.year, v.month, v.day)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
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
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
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
47
|
# 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
48
|
# the faster C-based MySQL/Ruby adapter (available both as a gem and from http://www.tmtm.org/en/mysql/ruby/).
|
134
49
|
#
|
@@ -142,49 +57,16 @@ module ActiveRecord
|
|
142
57
|
# * <tt>:database</tt> - The name of the database. No default, must be provided.
|
143
58
|
# * <tt>:encoding</tt> - (Optional) Sets the client encoding by executing "SET NAMES <encoding>" after connection.
|
144
59
|
# * <tt>:reconnect</tt> - Defaults to false (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/auto-reconnect.html).
|
60
|
+
# * <tt>:strict</tt> - Defaults to true. Enable STRICT_ALL_TABLES. (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/sql-mode.html)
|
61
|
+
# * <tt>:variables</tt> - (Optional) A hash session variables to send as <tt>SET @@SESSION.key = value</tt> on each database connection. Use the value +:default+ to set a variable to its DEFAULT value. (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/set-statement.html).
|
145
62
|
# * <tt>:sslca</tt> - Necessary to use MySQL with an SSL connection.
|
146
63
|
# * <tt>:sslkey</tt> - Necessary to use MySQL with an SSL connection.
|
147
64
|
# * <tt>:sslcert</tt> - Necessary to use MySQL with an SSL connection.
|
148
65
|
# * <tt>:sslcapath</tt> - Necessary to use MySQL with an SSL connection.
|
149
66
|
# * <tt>:sslcipher</tt> - Necessary to use MySQL with an SSL connection.
|
150
67
|
#
|
151
|
-
class MysqlAdapter <
|
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
|
-
ADAPTER_NAME = 'MySQL'
|
165
|
-
|
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
|
-
}
|
68
|
+
class MysqlAdapter < AbstractMysqlAdapter
|
69
|
+
ADAPTER_NAME = 'MySQL'.freeze
|
188
70
|
|
189
71
|
class StatementPool < ConnectionAdapters::StatementPool
|
190
72
|
def initialize(connection, max = 1000)
|
@@ -206,7 +88,7 @@ module ActiveRecord
|
|
206
88
|
end
|
207
89
|
|
208
90
|
def clear
|
209
|
-
cache.
|
91
|
+
cache.each_value do |hash|
|
210
92
|
hash[:stmt].close
|
211
93
|
end
|
212
94
|
cache.clear
|
@@ -214,122 +96,50 @@ module ActiveRecord
|
|
214
96
|
|
215
97
|
private
|
216
98
|
def cache
|
217
|
-
@cache[
|
99
|
+
@cache[Process.pid]
|
218
100
|
end
|
219
101
|
end
|
220
102
|
|
221
103
|
def initialize(connection, logger, connection_options, config)
|
222
|
-
super
|
223
|
-
@connection_options, @config = connection_options, config
|
224
|
-
@quoted_column_names, @quoted_table_names = {}, {}
|
225
|
-
@statements = {}
|
104
|
+
super
|
226
105
|
@statements = StatementPool.new(@connection,
|
227
|
-
config.fetch(:statement_limit) { 1000 })
|
106
|
+
self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 }))
|
228
107
|
@client_encoding = nil
|
229
108
|
connect
|
230
109
|
end
|
231
110
|
|
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
111
|
# Returns true, since this connection adapter supports prepared statement
|
255
112
|
# caching.
|
256
113
|
def supports_statement_cache?
|
257
114
|
true
|
258
115
|
end
|
259
116
|
|
260
|
-
#
|
261
|
-
def supports_migrations? #:nodoc:
|
262
|
-
true
|
263
|
-
end
|
264
|
-
|
265
|
-
# Returns true.
|
266
|
-
def supports_primary_key? #:nodoc:
|
267
|
-
true
|
268
|
-
end
|
117
|
+
# HELPER METHODS ===========================================
|
269
118
|
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
NATIVE_DATABASE_TYPES
|
277
|
-
end
|
278
|
-
|
279
|
-
|
280
|
-
# QUOTING ==================================================
|
281
|
-
|
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
|
-
elsif value.kind_of?(BigDecimal)
|
287
|
-
value.to_s("F")
|
119
|
+
def each_hash(result) # :nodoc:
|
120
|
+
if block_given?
|
121
|
+
result.each_hash do |row|
|
122
|
+
row.symbolize_keys!
|
123
|
+
yield row
|
124
|
+
end
|
288
125
|
else
|
289
|
-
|
126
|
+
to_enum(:each_hash, result)
|
290
127
|
end
|
291
128
|
end
|
292
129
|
|
293
|
-
def
|
294
|
-
|
295
|
-
|
296
|
-
value ? 1 : 0
|
297
|
-
end
|
298
|
-
|
299
|
-
def quote_column_name(name) #:nodoc:
|
300
|
-
@quoted_column_names[name] ||= "`#{name.to_s.gsub('`', '``')}`"
|
130
|
+
def error_number(exception) # :nodoc:
|
131
|
+
exception.errno if exception.respond_to?(:errno)
|
301
132
|
end
|
302
133
|
|
303
|
-
|
304
|
-
@quoted_table_names[name] ||= quote_column_name(name).gsub('.', '`.`')
|
305
|
-
end
|
134
|
+
# QUOTING ==================================================
|
306
135
|
|
307
136
|
def quote_string(string) #:nodoc:
|
308
137
|
@connection.quote(string)
|
309
138
|
end
|
310
139
|
|
311
|
-
|
312
|
-
QUOTED_TRUE
|
313
|
-
end
|
314
|
-
|
315
|
-
def quoted_false
|
316
|
-
QUOTED_FALSE
|
317
|
-
end
|
318
|
-
|
319
|
-
# REFERENTIAL INTEGRITY ====================================
|
320
|
-
|
321
|
-
def disable_referential_integrity #:nodoc:
|
322
|
-
old = select_value("SELECT @@FOREIGN_KEY_CHECKS")
|
323
|
-
|
324
|
-
begin
|
325
|
-
update("SET FOREIGN_KEY_CHECKS = 0")
|
326
|
-
yield
|
327
|
-
ensure
|
328
|
-
update("SET FOREIGN_KEY_CHECKS = #{old}")
|
329
|
-
end
|
330
|
-
end
|
331
|
-
|
140
|
+
#--
|
332
141
|
# CONNECTION MANAGEMENT ====================================
|
142
|
+
#++
|
333
143
|
|
334
144
|
def active?
|
335
145
|
if @connection.respond_to?(:stat)
|
@@ -349,14 +159,15 @@ module ActiveRecord
|
|
349
159
|
end
|
350
160
|
|
351
161
|
def reconnect!
|
162
|
+
super
|
352
163
|
disconnect!
|
353
|
-
clear_cache!
|
354
164
|
connect
|
355
165
|
end
|
356
166
|
|
357
167
|
# Disconnects from the database if already connected. Otherwise, this
|
358
168
|
# method does nothing.
|
359
169
|
def disconnect!
|
170
|
+
super
|
360
171
|
@connection.close rescue nil
|
361
172
|
end
|
362
173
|
|
@@ -369,632 +180,319 @@ module ActiveRecord
|
|
369
180
|
end
|
370
181
|
end
|
371
182
|
|
183
|
+
#--
|
372
184
|
# DATABASE STATEMENTS ======================================
|
185
|
+
#++
|
373
186
|
|
374
|
-
def select_rows(sql, name = nil)
|
187
|
+
def select_rows(sql, name = nil, binds = [])
|
375
188
|
@connection.query_with_result = true
|
376
|
-
rows =
|
189
|
+
rows = exec_query(sql, name, binds).rows
|
377
190
|
@connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
|
378
191
|
rows
|
379
192
|
end
|
380
193
|
|
381
194
|
# Clears the prepared statements cache.
|
382
195
|
def clear_cache!
|
196
|
+
super
|
383
197
|
@statements.clear
|
384
198
|
end
|
385
199
|
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
}
|
429
|
-
else
|
430
|
-
ENCODINGS = Hash.new { |h,k| h[k] = k }
|
431
|
-
end
|
200
|
+
# Taken from here:
|
201
|
+
# https://github.com/tmtm/ruby-mysql/blob/master/lib/mysql/charset.rb
|
202
|
+
# Author: TOMITA Masahiro <tommy@tmtm.org>
|
203
|
+
ENCODINGS = {
|
204
|
+
"armscii8" => nil,
|
205
|
+
"ascii" => Encoding::US_ASCII,
|
206
|
+
"big5" => Encoding::Big5,
|
207
|
+
"binary" => Encoding::ASCII_8BIT,
|
208
|
+
"cp1250" => Encoding::Windows_1250,
|
209
|
+
"cp1251" => Encoding::Windows_1251,
|
210
|
+
"cp1256" => Encoding::Windows_1256,
|
211
|
+
"cp1257" => Encoding::Windows_1257,
|
212
|
+
"cp850" => Encoding::CP850,
|
213
|
+
"cp852" => Encoding::CP852,
|
214
|
+
"cp866" => Encoding::IBM866,
|
215
|
+
"cp932" => Encoding::Windows_31J,
|
216
|
+
"dec8" => nil,
|
217
|
+
"eucjpms" => Encoding::EucJP_ms,
|
218
|
+
"euckr" => Encoding::EUC_KR,
|
219
|
+
"gb2312" => Encoding::EUC_CN,
|
220
|
+
"gbk" => Encoding::GBK,
|
221
|
+
"geostd8" => nil,
|
222
|
+
"greek" => Encoding::ISO_8859_7,
|
223
|
+
"hebrew" => Encoding::ISO_8859_8,
|
224
|
+
"hp8" => nil,
|
225
|
+
"keybcs2" => nil,
|
226
|
+
"koi8r" => Encoding::KOI8_R,
|
227
|
+
"koi8u" => Encoding::KOI8_U,
|
228
|
+
"latin1" => Encoding::ISO_8859_1,
|
229
|
+
"latin2" => Encoding::ISO_8859_2,
|
230
|
+
"latin5" => Encoding::ISO_8859_9,
|
231
|
+
"latin7" => Encoding::ISO_8859_13,
|
232
|
+
"macce" => Encoding::MacCentEuro,
|
233
|
+
"macroman" => Encoding::MacRoman,
|
234
|
+
"sjis" => Encoding::SHIFT_JIS,
|
235
|
+
"swe7" => nil,
|
236
|
+
"tis620" => Encoding::TIS_620,
|
237
|
+
"ucs2" => Encoding::UTF_16BE,
|
238
|
+
"ujis" => Encoding::EucJP_ms,
|
239
|
+
"utf8" => Encoding::UTF_8,
|
240
|
+
"utf8mb4" => Encoding::UTF_8,
|
241
|
+
}
|
432
242
|
|
433
243
|
# Get the client encoding for this database
|
434
244
|
def client_encoding
|
435
245
|
return @client_encoding if @client_encoding
|
436
246
|
|
437
247
|
result = exec_query(
|
438
|
-
"
|
248
|
+
"select @@character_set_client",
|
439
249
|
'SCHEMA')
|
440
250
|
@client_encoding = ENCODINGS[result.rows.last.last]
|
441
251
|
end
|
442
252
|
|
443
253
|
def exec_query(sql, name = 'SQL', binds = [])
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
254
|
+
if without_prepared_statement?(binds)
|
255
|
+
result_set, affected_rows = exec_without_stmt(sql, name)
|
256
|
+
else
|
257
|
+
result_set, affected_rows = exec_stmt(sql, name, binds)
|
448
258
|
end
|
259
|
+
|
260
|
+
yield affected_rows if block_given?
|
261
|
+
|
262
|
+
result_set
|
449
263
|
end
|
450
264
|
|
451
265
|
def last_inserted_id(result)
|
452
266
|
@connection.insert_id
|
453
267
|
end
|
454
268
|
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
269
|
+
module Fields # :nodoc:
|
270
|
+
class DateTime < Type::DateTime # :nodoc:
|
271
|
+
def cast_value(value)
|
272
|
+
if Mysql::Time === value
|
273
|
+
new_time(
|
274
|
+
value.year,
|
275
|
+
value.month,
|
276
|
+
value.day,
|
277
|
+
value.hour,
|
278
|
+
value.minute,
|
279
|
+
value.second,
|
280
|
+
value.second_part)
|
281
|
+
else
|
282
|
+
super
|
283
|
+
end
|
284
|
+
end
|
462
285
|
|
463
|
-
|
464
|
-
|
465
|
-
rows = result.to_a
|
466
|
-
result.free
|
286
|
+
def has_precision?
|
287
|
+
precision || 0
|
467
288
|
end
|
468
|
-
ActiveRecord::Result.new(cols, rows)
|
469
289
|
end
|
470
|
-
end
|
471
290
|
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
291
|
+
class Time < Type::Time # :nodoc:
|
292
|
+
def cast_value(value)
|
293
|
+
if Mysql::Time === value
|
294
|
+
new_time(
|
295
|
+
2000,
|
296
|
+
01,
|
297
|
+
01,
|
298
|
+
value.hour,
|
299
|
+
value.minute,
|
300
|
+
value.second,
|
301
|
+
value.second_part)
|
302
|
+
else
|
303
|
+
super
|
304
|
+
end
|
305
|
+
end
|
485
306
|
end
|
486
|
-
end
|
487
307
|
|
488
|
-
|
489
|
-
|
490
|
-
id_value || @connection.insert_id
|
491
|
-
end
|
492
|
-
alias :create :insert_sql
|
308
|
+
class << self
|
309
|
+
TYPES = Type::HashLookupTypeMap.new # :nodoc:
|
493
310
|
|
494
|
-
|
495
|
-
super
|
496
|
-
@connection.affected_rows
|
497
|
-
end
|
311
|
+
delegate :register_type, :alias_type, to: :TYPES
|
498
312
|
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
313
|
+
def find_type(field)
|
314
|
+
if field.type == Mysql::Field::TYPE_TINY && field.length > 1
|
315
|
+
TYPES.lookup(Mysql::Field::TYPE_LONG)
|
316
|
+
else
|
317
|
+
TYPES.lookup(field.type)
|
318
|
+
end
|
503
319
|
end
|
504
320
|
end
|
505
|
-
end
|
506
|
-
alias :exec_update :exec_delete
|
507
321
|
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
end
|
322
|
+
register_type Mysql::Field::TYPE_TINY, Type::Boolean.new
|
323
|
+
register_type Mysql::Field::TYPE_LONG, Type::Integer.new
|
324
|
+
alias_type Mysql::Field::TYPE_LONGLONG, Mysql::Field::TYPE_LONG
|
325
|
+
alias_type Mysql::Field::TYPE_NEWDECIMAL, Mysql::Field::TYPE_LONG
|
513
326
|
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
327
|
+
register_type Mysql::Field::TYPE_DATE, Type::Date.new
|
328
|
+
register_type Mysql::Field::TYPE_DATETIME, Fields::DateTime.new
|
329
|
+
register_type Mysql::Field::TYPE_TIME, Fields::Time.new
|
330
|
+
register_type Mysql::Field::TYPE_FLOAT, Type::Float.new
|
518
331
|
end
|
519
332
|
|
520
|
-
def
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
def create_savepoint
|
527
|
-
execute("SAVEPOINT #{current_savepoint_name}")
|
528
|
-
end
|
529
|
-
|
530
|
-
def rollback_to_savepoint
|
531
|
-
execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
|
532
|
-
end
|
533
|
-
|
534
|
-
def release_savepoint
|
535
|
-
execute("RELEASE SAVEPOINT #{current_savepoint_name}")
|
536
|
-
end
|
537
|
-
|
538
|
-
def add_limit_offset!(sql, options) #:nodoc:
|
539
|
-
limit, offset = options[:limit], options[:offset]
|
540
|
-
if limit && offset
|
541
|
-
sql << " LIMIT #{offset.to_i}, #{sanitize_limit(limit)}"
|
542
|
-
elsif limit
|
543
|
-
sql << " LIMIT #{sanitize_limit(limit)}"
|
544
|
-
elsif offset
|
545
|
-
sql << " OFFSET #{offset.to_i}"
|
546
|
-
end
|
547
|
-
sql
|
548
|
-
end
|
549
|
-
deprecate :add_limit_offset!
|
550
|
-
|
551
|
-
# In the simple case, MySQL allows us to place JOINs directly into the UPDATE
|
552
|
-
# query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
|
553
|
-
# these, we must use a subquery. However, MySQL is too stupid to create a
|
554
|
-
# temporary table for this automatically, so we have to give it some prompting
|
555
|
-
# in the form of a subsubquery. Ugh!
|
556
|
-
def join_to_update(update, select) #:nodoc:
|
557
|
-
if select.limit || select.offset || select.orders.any?
|
558
|
-
subsubselect = select.clone
|
559
|
-
subsubselect.projections = [update.key]
|
560
|
-
|
561
|
-
subselect = Arel::SelectManager.new(select.engine)
|
562
|
-
subselect.project Arel.sql(update.key.name)
|
563
|
-
subselect.from subsubselect.as('__active_record_temp')
|
564
|
-
|
565
|
-
update.where update.key.in(subselect)
|
566
|
-
else
|
567
|
-
update.table select.source
|
568
|
-
update.wheres = select.constraints
|
333
|
+
def initialize_type_map(m) # :nodoc:
|
334
|
+
super
|
335
|
+
m.register_type %r(time)i, Fields::Time.new
|
336
|
+
m.register_type(%r(datetime)i) do |sql_type|
|
337
|
+
precision = extract_precision(sql_type)
|
338
|
+
Fields::DateTime.new(precision: precision)
|
569
339
|
end
|
570
340
|
end
|
571
341
|
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
sql = "SHOW TABLES"
|
579
|
-
end
|
580
|
-
|
581
|
-
select_all(sql).map do |table|
|
582
|
-
table.delete('Table_type')
|
583
|
-
sql = "SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}"
|
584
|
-
exec_without_stmt(sql).first['Create Table'] + ";\n\n"
|
585
|
-
end.join("")
|
586
|
-
end
|
342
|
+
def exec_without_stmt(sql, name = 'SQL') # :nodoc:
|
343
|
+
# Some queries, like SHOW CREATE TABLE don't work through the prepared
|
344
|
+
# statement API. For those queries, we need to use this method. :'(
|
345
|
+
log(sql, name) do
|
346
|
+
result = @connection.query(sql)
|
347
|
+
affected_rows = @connection.affected_rows
|
587
348
|
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
349
|
+
if result
|
350
|
+
types = {}
|
351
|
+
fields = []
|
352
|
+
result.fetch_fields.each { |field|
|
353
|
+
field_name = field.name
|
354
|
+
fields << field_name
|
355
|
+
|
356
|
+
if field.decimals > 0
|
357
|
+
types[field_name] = Type::Decimal.new
|
358
|
+
else
|
359
|
+
types[field_name] = Fields.find_type field
|
360
|
+
end
|
361
|
+
}
|
362
|
+
|
363
|
+
result_set = ActiveRecord::Result.new(fields, result.to_a, types)
|
364
|
+
result.free
|
365
|
+
else
|
366
|
+
result_set = ActiveRecord::Result.new([], [])
|
367
|
+
end
|
594
368
|
|
595
|
-
|
596
|
-
# Charset defaults to utf8.
|
597
|
-
#
|
598
|
-
# Example:
|
599
|
-
# create_database 'charset_test', :charset => 'latin1', :collation => 'latin1_bin'
|
600
|
-
# create_database 'matt_development'
|
601
|
-
# create_database 'matt_development', :charset => :big5
|
602
|
-
def create_database(name, options = {})
|
603
|
-
if options[:collation]
|
604
|
-
execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}` COLLATE `#{options[:collation]}`"
|
605
|
-
else
|
606
|
-
execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}`"
|
369
|
+
[result_set, affected_rows]
|
607
370
|
end
|
608
371
|
end
|
609
372
|
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
# drop_database 'sebastian_development'
|
614
|
-
def drop_database(name) #:nodoc:
|
615
|
-
execute "DROP DATABASE IF EXISTS `#{name}`"
|
616
|
-
end
|
617
|
-
|
618
|
-
def current_database
|
619
|
-
select_value 'SELECT DATABASE() as db'
|
620
|
-
end
|
621
|
-
|
622
|
-
# Returns the database character set.
|
623
|
-
def charset
|
624
|
-
show_variable 'character_set_database'
|
625
|
-
end
|
626
|
-
|
627
|
-
# Returns the database collation strategy.
|
628
|
-
def collation
|
629
|
-
show_variable 'collation_database'
|
630
|
-
end
|
631
|
-
|
632
|
-
def tables(name = nil, database = nil) #:nodoc:
|
633
|
-
sql = "SHOW TABLES "
|
634
|
-
sql << "IN #{quote_table_name(database)} " if database
|
635
|
-
|
636
|
-
result = execute(sql, 'SCHEMA')
|
637
|
-
tables = result.collect { |field| field[0] }
|
373
|
+
def execute_and_free(sql, name = nil) # :nodoc:
|
374
|
+
result = execute(sql, name)
|
375
|
+
ret = yield result
|
638
376
|
result.free
|
639
|
-
|
377
|
+
ret
|
640
378
|
end
|
641
379
|
|
642
|
-
def
|
643
|
-
|
644
|
-
|
645
|
-
name = name.to_s
|
646
|
-
schema, table = name.split('.', 2)
|
647
|
-
|
648
|
-
unless table # A table was provided without a schema
|
649
|
-
table = schema
|
650
|
-
schema = nil
|
651
|
-
end
|
652
|
-
|
653
|
-
tables(nil, schema).include? table
|
654
|
-
end
|
655
|
-
|
656
|
-
def drop_table(table_name, options = {})
|
657
|
-
super(table_name, options)
|
380
|
+
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
|
381
|
+
super sql, name
|
382
|
+
id_value || @connection.insert_id
|
658
383
|
end
|
384
|
+
alias :create :insert_sql
|
659
385
|
|
660
|
-
|
661
|
-
|
662
|
-
indexes = []
|
663
|
-
current_index = nil
|
664
|
-
result = execute("SHOW KEYS FROM #{quote_table_name(table_name)}", name)
|
665
|
-
result.each do |row|
|
666
|
-
if current_index != row[2]
|
667
|
-
next if row[2] == "PRIMARY" # skip the primary key
|
668
|
-
current_index = row[2]
|
669
|
-
indexes << IndexDefinition.new(row[0], row[2], row[1] == "0", [], [])
|
670
|
-
end
|
386
|
+
def exec_delete(sql, name, binds) # :nodoc:
|
387
|
+
affected_rows = 0
|
671
388
|
|
672
|
-
|
673
|
-
|
389
|
+
exec_query(sql, name, binds) do |n|
|
390
|
+
affected_rows = n
|
674
391
|
end
|
675
|
-
result.free
|
676
|
-
indexes
|
677
|
-
end
|
678
392
|
|
679
|
-
|
680
|
-
def columns(table_name, name = nil)#:nodoc:
|
681
|
-
sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}"
|
682
|
-
result = execute(sql, 'SCHEMA')
|
683
|
-
columns = result.collect { |field| MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") }
|
684
|
-
result.free
|
685
|
-
columns
|
393
|
+
affected_rows
|
686
394
|
end
|
395
|
+
alias :exec_update :exec_delete
|
687
396
|
|
688
|
-
def
|
689
|
-
|
397
|
+
def begin_db_transaction #:nodoc:
|
398
|
+
exec_query "BEGIN"
|
690
399
|
end
|
691
400
|
|
692
|
-
|
693
|
-
#
|
694
|
-
# Example:
|
695
|
-
# rename_table('octopuses', 'octopi')
|
696
|
-
def rename_table(table_name, new_name)
|
697
|
-
execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
|
698
|
-
end
|
401
|
+
private
|
699
402
|
|
700
|
-
def
|
701
|
-
|
702
|
-
|
703
|
-
|
403
|
+
def exec_stmt(sql, name, binds)
|
404
|
+
cache = {}
|
405
|
+
type_casted_binds = binds.map { |col, val|
|
406
|
+
[col, type_cast(val, col)]
|
407
|
+
}
|
704
408
|
|
705
|
-
|
706
|
-
|
409
|
+
log(sql, name, type_casted_binds) do
|
410
|
+
if binds.empty?
|
411
|
+
stmt = @connection.prepare(sql)
|
707
412
|
else
|
708
|
-
|
413
|
+
cache = @statements[sql] ||= {
|
414
|
+
:stmt => @connection.prepare(sql)
|
415
|
+
}
|
416
|
+
stmt = cache[:stmt]
|
709
417
|
end
|
710
|
-
end.flatten.join(", ")
|
711
418
|
|
712
|
-
|
713
|
-
|
419
|
+
begin
|
420
|
+
stmt.execute(*type_casted_binds.map { |_, val| val })
|
421
|
+
rescue Mysql::Error => e
|
422
|
+
# Older versions of MySQL leave the prepared statement in a bad
|
423
|
+
# place when an error occurs. To support older MySQL versions, we
|
424
|
+
# need to close the statement and delete the statement from the
|
425
|
+
# cache.
|
426
|
+
stmt.close
|
427
|
+
@statements.delete sql
|
428
|
+
raise e
|
429
|
+
end
|
714
430
|
|
715
|
-
|
716
|
-
|
717
|
-
|
431
|
+
cols = nil
|
432
|
+
if metadata = stmt.result_metadata
|
433
|
+
cols = cache[:cols] ||= metadata.fetch_fields.map { |field|
|
434
|
+
field.name
|
435
|
+
}
|
436
|
+
metadata.free
|
437
|
+
end
|
718
438
|
|
719
|
-
|
720
|
-
|
721
|
-
change_column table_name, column_name, column.sql_type, :default => default
|
722
|
-
end
|
439
|
+
result_set = ActiveRecord::Result.new(cols, stmt.to_a) if cols
|
440
|
+
affected_rows = stmt.affected_rows
|
723
441
|
|
724
|
-
|
725
|
-
|
442
|
+
stmt.free_result
|
443
|
+
stmt.close if binds.empty?
|
726
444
|
|
727
|
-
|
728
|
-
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
445
|
+
[result_set, affected_rows]
|
729
446
|
end
|
730
|
-
|
731
|
-
change_column table_name, column_name, column.sql_type, :null => null
|
732
|
-
end
|
733
|
-
|
734
|
-
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
735
|
-
execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_sql(table_name, column_name, type, options)}")
|
736
|
-
end
|
737
|
-
|
738
|
-
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
739
|
-
execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_sql(table_name, column_name, new_column_name)}")
|
740
447
|
end
|
741
448
|
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
case limit
|
747
|
-
when 1; 'tinyint'
|
748
|
-
when 2; 'smallint'
|
749
|
-
when 3; 'mediumint'
|
750
|
-
when nil, 4, 11; 'int(11)' # compatibility with MySQL default
|
751
|
-
when 5..8; 'bigint'
|
752
|
-
else raise(ActiveRecordError, "No integer type has byte size #{limit}")
|
753
|
-
end
|
754
|
-
when 'text'
|
755
|
-
case limit
|
756
|
-
when 0..0xff; 'tinytext'
|
757
|
-
when nil, 0x100..0xffff; 'text'
|
758
|
-
when 0x10000..0xffffff; 'mediumtext'
|
759
|
-
when 0x1000000..0xffffffff; 'longtext'
|
760
|
-
else raise(ActiveRecordError, "No text type has character length #{limit}")
|
761
|
-
end
|
762
|
-
else
|
763
|
-
super
|
449
|
+
def connect
|
450
|
+
encoding = @config[:encoding]
|
451
|
+
if encoding
|
452
|
+
@connection.options(Mysql::SET_CHARSET_NAME, encoding) rescue nil
|
764
453
|
end
|
765
|
-
end
|
766
454
|
|
767
|
-
|
768
|
-
|
769
|
-
sql << " FIRST"
|
770
|
-
elsif options[:after]
|
771
|
-
sql << " AFTER #{quote_column_name(options[:after])}"
|
455
|
+
if @config[:sslca] || @config[:sslkey]
|
456
|
+
@connection.ssl_set(@config[:sslkey], @config[:sslcert], @config[:sslca], @config[:sslcapath], @config[:sslcipher])
|
772
457
|
end
|
773
|
-
end
|
774
458
|
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
variables.first['Value'] unless variables.empty?
|
779
|
-
end
|
459
|
+
@connection.options(Mysql::OPT_CONNECT_TIMEOUT, @config[:connect_timeout]) if @config[:connect_timeout]
|
460
|
+
@connection.options(Mysql::OPT_READ_TIMEOUT, @config[:read_timeout]) if @config[:read_timeout]
|
461
|
+
@connection.options(Mysql::OPT_WRITE_TIMEOUT, @config[:write_timeout]) if @config[:write_timeout]
|
780
462
|
|
781
|
-
|
782
|
-
def pk_and_sequence_for(table) #:nodoc:
|
783
|
-
result = execute("SHOW CREATE TABLE #{quote_table_name(table)}", 'SCHEMA')
|
784
|
-
create_table = result.fetch_hash["Create Table"]
|
785
|
-
result.free
|
463
|
+
@connection.real_connect(*@connection_options)
|
786
464
|
|
787
|
-
|
788
|
-
|
789
|
-
keys.length == 1 ? [keys.first, nil] : nil
|
790
|
-
else
|
791
|
-
nil
|
792
|
-
end
|
793
|
-
end
|
465
|
+
# reconnect must be set after real_connect is called, because real_connect sets it to false internally
|
466
|
+
@connection.reconnect = !!@config[:reconnect] if @connection.respond_to?(:reconnect=)
|
794
467
|
|
795
|
-
|
796
|
-
def primary_key(table)
|
797
|
-
pk_and_sequence = pk_and_sequence_for(table)
|
798
|
-
pk_and_sequence && pk_and_sequence.first
|
468
|
+
configure_connection
|
799
469
|
end
|
800
470
|
|
801
|
-
|
802
|
-
|
471
|
+
# Many Rails applications monkey-patch a replacement of the configure_connection method
|
472
|
+
# and don't call 'super', so leave this here even though it looks superfluous.
|
473
|
+
def configure_connection
|
474
|
+
super
|
803
475
|
end
|
804
|
-
deprecate :case_sensitive_equality_operator
|
805
476
|
|
806
|
-
def
|
807
|
-
|
477
|
+
def select(sql, name = nil, binds = [])
|
478
|
+
@connection.query_with_result = true
|
479
|
+
rows = super
|
480
|
+
@connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
|
481
|
+
rows
|
808
482
|
end
|
809
483
|
|
810
|
-
|
811
|
-
|
484
|
+
# Returns the full version of the connected MySQL server.
|
485
|
+
def full_version
|
486
|
+
@full_version ||= @connection.server_info
|
812
487
|
end
|
813
488
|
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
case length
|
819
|
-
when Hash
|
820
|
-
column_names.map {|name| length[name] ? "#{quote_column_name(name)}(#{length[name]})" : quote_column_name(name) }
|
821
|
-
when Fixnum
|
822
|
-
column_names.map {|name| "#{quote_column_name(name)}(#{length})"}
|
823
|
-
else
|
824
|
-
column_names.map {|name| quote_column_name(name) }
|
825
|
-
end
|
826
|
-
end
|
827
|
-
|
828
|
-
def translate_exception(exception, message)
|
829
|
-
return super unless exception.respond_to?(:errno)
|
830
|
-
|
831
|
-
case exception.errno
|
832
|
-
when 1062
|
833
|
-
RecordNotUnique.new(message, exception)
|
834
|
-
when 1452
|
835
|
-
InvalidForeignKey.new(message, exception)
|
836
|
-
else
|
837
|
-
super
|
838
|
-
end
|
839
|
-
end
|
840
|
-
|
841
|
-
def add_column_sql(table_name, column_name, type, options = {})
|
842
|
-
add_column_sql = "ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
843
|
-
add_column_options!(add_column_sql, options)
|
844
|
-
add_column_position!(add_column_sql, options)
|
845
|
-
add_column_sql
|
846
|
-
end
|
847
|
-
|
848
|
-
def remove_column_sql(table_name, *column_names)
|
849
|
-
columns_for_remove(table_name, *column_names).map {|column_name| "DROP #{column_name}" }
|
850
|
-
end
|
851
|
-
alias :remove_columns_sql :remove_column
|
852
|
-
|
853
|
-
def change_column_sql(table_name, column_name, type, options = {})
|
854
|
-
column = column_for(table_name, column_name)
|
855
|
-
|
856
|
-
unless options_include_default?(options)
|
857
|
-
options[:default] = column.default
|
858
|
-
end
|
859
|
-
|
860
|
-
unless options.has_key?(:null)
|
861
|
-
options[:null] = column.null
|
862
|
-
end
|
863
|
-
|
864
|
-
change_column_sql = "CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
865
|
-
add_column_options!(change_column_sql, options)
|
866
|
-
add_column_position!(change_column_sql, options)
|
867
|
-
change_column_sql
|
489
|
+
def set_field_encoding field_name
|
490
|
+
field_name.force_encoding(client_encoding)
|
491
|
+
if internal_enc = Encoding.default_internal
|
492
|
+
field_name = field_name.encode!(internal_enc)
|
868
493
|
end
|
869
|
-
|
870
|
-
def rename_column_sql(table_name, column_name, new_column_name)
|
871
|
-
options = {}
|
872
|
-
|
873
|
-
if column = columns(table_name).find { |c| c.name == column_name.to_s }
|
874
|
-
options[:default] = column.default
|
875
|
-
options[:null] = column.null
|
876
|
-
else
|
877
|
-
raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
|
878
|
-
end
|
879
|
-
|
880
|
-
current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
|
881
|
-
rename_column_sql = "CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
|
882
|
-
add_column_options!(rename_column_sql, options)
|
883
|
-
rename_column_sql
|
884
|
-
end
|
885
|
-
|
886
|
-
def add_index_sql(table_name, column_name, options = {})
|
887
|
-
index_name, index_type, index_columns = add_index_options(table_name, column_name, options)
|
888
|
-
"ADD #{index_type} INDEX #{index_name} (#{index_columns})"
|
889
|
-
end
|
890
|
-
|
891
|
-
def remove_index_sql(table_name, options = {})
|
892
|
-
index_name = index_name_for_remove(table_name, options)
|
893
|
-
"DROP INDEX #{index_name}"
|
894
|
-
end
|
895
|
-
|
896
|
-
def add_timestamps_sql(table_name)
|
897
|
-
[add_column_sql(table_name, :created_at, :datetime), add_column_sql(table_name, :updated_at, :datetime)]
|
898
|
-
end
|
899
|
-
|
900
|
-
def remove_timestamps_sql(table_name)
|
901
|
-
[remove_column_sql(table_name, :updated_at), remove_column_sql(table_name, :created_at)]
|
902
|
-
end
|
903
|
-
|
904
|
-
private
|
905
|
-
def exec_stmt(sql, name, binds)
|
906
|
-
cache = {}
|
907
|
-
if binds.empty?
|
908
|
-
stmt = @connection.prepare(sql)
|
909
|
-
else
|
910
|
-
cache = @statements[sql] ||= {
|
911
|
-
:stmt => @connection.prepare(sql)
|
912
|
-
}
|
913
|
-
stmt = cache[:stmt]
|
914
|
-
end
|
915
|
-
|
916
|
-
|
917
|
-
begin
|
918
|
-
stmt.execute(*binds.map { |col, val| type_cast(val, col) })
|
919
|
-
rescue Mysql::Error => e
|
920
|
-
# Older versions of MySQL leave the prepared statement in a bad
|
921
|
-
# place when an error occurs. To support older mysql versions, we
|
922
|
-
# need to close the statement and delete the statement from the
|
923
|
-
# cache.
|
924
|
-
stmt.close
|
925
|
-
@statements.delete sql
|
926
|
-
raise e
|
927
|
-
end
|
928
|
-
|
929
|
-
cols = nil
|
930
|
-
if metadata = stmt.result_metadata
|
931
|
-
cols = cache[:cols] ||= metadata.fetch_fields.map { |field|
|
932
|
-
field.name
|
933
|
-
}
|
934
|
-
end
|
935
|
-
|
936
|
-
result = yield [cols, stmt]
|
937
|
-
|
938
|
-
stmt.result_metadata.free if cols
|
939
|
-
stmt.free_result
|
940
|
-
stmt.close if binds.empty?
|
941
|
-
|
942
|
-
result
|
494
|
+
field_name
|
943
495
|
end
|
944
|
-
|
945
|
-
def connect
|
946
|
-
encoding = @config[:encoding]
|
947
|
-
if encoding
|
948
|
-
@connection.options(Mysql::SET_CHARSET_NAME, encoding) rescue nil
|
949
|
-
end
|
950
|
-
|
951
|
-
if @config[:sslca] || @config[:sslkey]
|
952
|
-
@connection.ssl_set(@config[:sslkey], @config[:sslcert], @config[:sslca], @config[:sslcapath], @config[:sslcipher])
|
953
|
-
end
|
954
|
-
|
955
|
-
@connection.options(Mysql::OPT_CONNECT_TIMEOUT, @config[:connect_timeout]) if @config[:connect_timeout]
|
956
|
-
@connection.options(Mysql::OPT_READ_TIMEOUT, @config[:read_timeout]) if @config[:read_timeout]
|
957
|
-
@connection.options(Mysql::OPT_WRITE_TIMEOUT, @config[:write_timeout]) if @config[:write_timeout]
|
958
|
-
|
959
|
-
@connection.real_connect(*@connection_options)
|
960
|
-
|
961
|
-
# reconnect must be set after real_connect is called, because real_connect sets it to false internally
|
962
|
-
@connection.reconnect = !!@config[:reconnect] if @connection.respond_to?(:reconnect=)
|
963
|
-
|
964
|
-
configure_connection
|
965
|
-
end
|
966
|
-
|
967
|
-
def configure_connection
|
968
|
-
encoding = @config[:encoding]
|
969
|
-
execute("SET NAMES '#{encoding}'", :skip_logging) if encoding
|
970
|
-
|
971
|
-
# By default, MySQL 'where id is null' selects the last inserted id.
|
972
|
-
# Turn this off. http://dev.rubyonrails.org/ticket/6778
|
973
|
-
execute("SET SQL_AUTO_IS_NULL=0", :skip_logging)
|
974
|
-
end
|
975
|
-
|
976
|
-
def select(sql, name = nil, binds = [])
|
977
|
-
@connection.query_with_result = true
|
978
|
-
rows = exec_query(sql, name, binds).to_a
|
979
|
-
@connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
|
980
|
-
rows
|
981
|
-
end
|
982
|
-
|
983
|
-
def supports_views?
|
984
|
-
version[0] >= 5
|
985
|
-
end
|
986
|
-
|
987
|
-
# Returns the version of the connected MySQL server.
|
988
|
-
def version
|
989
|
-
@version ||= @connection.server_info.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
|
990
|
-
end
|
991
|
-
|
992
|
-
def column_for(table_name, column_name)
|
993
|
-
unless column = columns(table_name).find { |c| c.name == column_name.to_s }
|
994
|
-
raise "No such column: #{table_name}.#{column_name}"
|
995
|
-
end
|
996
|
-
column
|
997
|
-
end
|
998
496
|
end
|
999
497
|
end
|
1000
498
|
end
|