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,248 +1,179 @@
|
|
1
|
-
|
2
|
-
require 'arel/visitors/bind_visitor'
|
1
|
+
require 'active_record/connection_adapters/abstract_mysql_adapter'
|
3
2
|
|
4
|
-
gem 'mysql2', '
|
3
|
+
gem 'mysql2', '>= 0.3.13', '< 0.6.0'
|
5
4
|
require 'mysql2'
|
6
5
|
|
7
6
|
module ActiveRecord
|
8
|
-
|
7
|
+
module ConnectionHandling # :nodoc:
|
9
8
|
# Establishes a connection to the database that's used by all Active Record objects.
|
10
|
-
def
|
9
|
+
def mysql2_connection(config)
|
10
|
+
config = config.symbolize_keys
|
11
|
+
|
11
12
|
config[:username] = 'root' if config[:username].nil?
|
12
13
|
|
13
14
|
if Mysql2::Client.const_defined? :FOUND_ROWS
|
14
15
|
config[:flags] = Mysql2::Client::FOUND_ROWS
|
15
16
|
end
|
16
17
|
|
17
|
-
client = Mysql2::Client.new(config
|
18
|
+
client = Mysql2::Client.new(config)
|
18
19
|
options = [config[:host], config[:username], config[:password], config[:database], config[:port], config[:socket], 0]
|
19
20
|
ConnectionAdapters::Mysql2Adapter.new(client, logger, options, config)
|
21
|
+
rescue Mysql2::Error => error
|
22
|
+
if error.message.include?("Unknown database")
|
23
|
+
raise ActiveRecord::NoDatabaseError.new(error.message, error)
|
24
|
+
else
|
25
|
+
raise
|
26
|
+
end
|
20
27
|
end
|
21
28
|
end
|
22
29
|
|
23
30
|
module ConnectionAdapters
|
24
|
-
class
|
25
|
-
|
31
|
+
class Mysql2Adapter < AbstractMysqlAdapter
|
32
|
+
ADAPTER_NAME = 'Mysql2'.freeze
|
26
33
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
nil
|
34
|
+
def initialize(connection, logger, connection_options, config)
|
35
|
+
super
|
36
|
+
@prepared_statements = false
|
37
|
+
configure_connection
|
38
|
+
end
|
39
|
+
|
40
|
+
MAX_INDEX_LENGTH_FOR_UTF8MB4 = 191
|
41
|
+
def initialize_schema_migrations_table
|
42
|
+
if charset == 'utf8mb4'
|
43
|
+
ActiveRecord::SchemaMigration.create_table(MAX_INDEX_LENGTH_FOR_UTF8MB4)
|
38
44
|
else
|
39
|
-
|
45
|
+
ActiveRecord::SchemaMigration.create_table
|
40
46
|
end
|
41
47
|
end
|
42
48
|
|
43
|
-
def
|
44
|
-
|
45
|
-
super
|
49
|
+
def supports_explain?
|
50
|
+
true
|
46
51
|
end
|
47
52
|
|
48
|
-
|
49
|
-
def simplified_type(field_type)
|
50
|
-
return :boolean if Mysql2Adapter.emulate_booleans && field_type.downcase.index(BOOL)
|
51
|
-
|
52
|
-
case field_type
|
53
|
-
when /enum/i, /set/i then :string
|
54
|
-
when /year/i then :integer
|
55
|
-
when /bit/i then :binary
|
56
|
-
else
|
57
|
-
super
|
58
|
-
end
|
59
|
-
end
|
53
|
+
# HELPER METHODS ===========================================
|
60
54
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
when /tiny/i
|
66
|
-
255
|
67
|
-
when /medium/i
|
68
|
-
16777215
|
69
|
-
when /long/i
|
70
|
-
2147483647 # mysql only allows 2^31-1, not 2^32-1, somewhat inconsistently with the tiny/medium/normal cases
|
71
|
-
else
|
72
|
-
super # we could return 65535 here, but we leave it undecorated by default
|
73
|
-
end
|
74
|
-
when /^bigint/i; 8
|
75
|
-
when /^int/i; 4
|
76
|
-
when /^mediumint/i; 3
|
77
|
-
when /^smallint/i; 2
|
78
|
-
when /^tinyint/i; 1
|
79
|
-
else
|
80
|
-
super
|
55
|
+
def each_hash(result) # :nodoc:
|
56
|
+
if block_given?
|
57
|
+
result.each(:as => :hash, :symbolize_keys => true) do |row|
|
58
|
+
yield row
|
81
59
|
end
|
60
|
+
else
|
61
|
+
to_enum(:each_hash, result)
|
82
62
|
end
|
83
|
-
|
84
|
-
# MySQL misreports NOT NULL column default when none is given.
|
85
|
-
# We can't detect this for columns which may have a legitimate ''
|
86
|
-
# default (string) but we can for others (integer, datetime, boolean,
|
87
|
-
# and the rest).
|
88
|
-
#
|
89
|
-
# Test whether the column has default '', is not null, and is not
|
90
|
-
# a type allowing default ''.
|
91
|
-
def missing_default_forged_as_empty_string?(default)
|
92
|
-
type != :string && !null && default == ''
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
class Mysql2Adapter < AbstractAdapter
|
97
|
-
cattr_accessor :emulate_booleans
|
98
|
-
self.emulate_booleans = true
|
99
|
-
|
100
|
-
ADAPTER_NAME = 'Mysql2'
|
101
|
-
PRIMARY = "PRIMARY"
|
102
|
-
|
103
|
-
LOST_CONNECTION_ERROR_MESSAGES = [
|
104
|
-
"Server shutdown in progress",
|
105
|
-
"Broken pipe",
|
106
|
-
"Lost connection to MySQL server during query",
|
107
|
-
"MySQL server has gone away" ]
|
108
|
-
|
109
|
-
QUOTED_TRUE, QUOTED_FALSE = '1', '0'
|
110
|
-
|
111
|
-
NATIVE_DATABASE_TYPES = {
|
112
|
-
:primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY",
|
113
|
-
:string => { :name => "varchar", :limit => 255 },
|
114
|
-
:text => { :name => "text" },
|
115
|
-
:integer => { :name => "int", :limit => 4 },
|
116
|
-
:float => { :name => "float" },
|
117
|
-
:decimal => { :name => "decimal" },
|
118
|
-
:datetime => { :name => "datetime" },
|
119
|
-
:timestamp => { :name => "datetime" },
|
120
|
-
:time => { :name => "time" },
|
121
|
-
:date => { :name => "date" },
|
122
|
-
:binary => { :name => "blob" },
|
123
|
-
:boolean => { :name => "tinyint", :limit => 1 }
|
124
|
-
}
|
125
|
-
|
126
|
-
def initialize(connection, logger, connection_options, config)
|
127
|
-
super(connection, logger)
|
128
|
-
@connection_options, @config = connection_options, config
|
129
|
-
@quoted_column_names, @quoted_table_names = {}, {}
|
130
|
-
configure_connection
|
131
|
-
end
|
132
|
-
|
133
|
-
class BindSubstitution < Arel::Visitors::MySQL # :nodoc:
|
134
|
-
include Arel::Visitors::BindVisitor
|
135
63
|
end
|
136
64
|
|
137
|
-
def
|
138
|
-
|
65
|
+
def error_number(exception)
|
66
|
+
exception.error_number if exception.respond_to?(:error_number)
|
139
67
|
end
|
140
68
|
|
141
|
-
|
142
|
-
|
143
|
-
|
69
|
+
#--
|
70
|
+
# QUOTING ==================================================
|
71
|
+
#++
|
144
72
|
|
145
|
-
|
146
|
-
|
147
|
-
true
|
73
|
+
def quote_string(string)
|
74
|
+
@connection.escape(string)
|
148
75
|
end
|
149
76
|
|
150
|
-
|
151
|
-
|
152
|
-
|
77
|
+
#--
|
78
|
+
# CONNECTION MANAGEMENT ====================================
|
79
|
+
#++
|
153
80
|
|
154
|
-
|
155
|
-
|
156
|
-
true
|
81
|
+
def active?
|
82
|
+
@connection.ping
|
157
83
|
end
|
158
84
|
|
159
|
-
def
|
160
|
-
|
85
|
+
def reconnect!
|
86
|
+
super
|
87
|
+
disconnect!
|
88
|
+
connect
|
161
89
|
end
|
90
|
+
alias :reset! :reconnect!
|
162
91
|
|
163
|
-
#
|
164
|
-
|
165
|
-
def
|
166
|
-
|
167
|
-
|
168
|
-
"x'#{s}'"
|
169
|
-
elsif value.kind_of?(BigDecimal)
|
170
|
-
value.to_s("F")
|
171
|
-
else
|
172
|
-
super
|
173
|
-
end
|
92
|
+
# Disconnects from the database if already connected.
|
93
|
+
# Otherwise, this method does nothing.
|
94
|
+
def disconnect!
|
95
|
+
super
|
96
|
+
@connection.close
|
174
97
|
end
|
175
98
|
|
176
|
-
|
177
|
-
|
178
|
-
|
99
|
+
#--
|
100
|
+
# DATABASE STATEMENTS ======================================
|
101
|
+
#++
|
179
102
|
|
180
|
-
def
|
181
|
-
|
182
|
-
|
103
|
+
def explain(arel, binds = [])
|
104
|
+
sql = "EXPLAIN #{to_sql(arel, binds.dup)}"
|
105
|
+
start = Time.now
|
106
|
+
result = exec_query(sql, 'EXPLAIN', binds)
|
107
|
+
elapsed = Time.now - start
|
183
108
|
|
184
|
-
|
185
|
-
@connection.escape(string)
|
109
|
+
ExplainPrettyPrinter.new.pp(result, elapsed)
|
186
110
|
end
|
187
111
|
|
188
|
-
|
189
|
-
|
190
|
-
|
112
|
+
class ExplainPrettyPrinter # :nodoc:
|
113
|
+
# Pretty prints the result of a EXPLAIN in a way that resembles the output of the
|
114
|
+
# MySQL shell:
|
115
|
+
#
|
116
|
+
# +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
|
117
|
+
# | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
|
118
|
+
# +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
|
119
|
+
# | 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | |
|
120
|
+
# | 1 | SIMPLE | posts | ALL | NULL | NULL | NULL | NULL | 1 | Using where |
|
121
|
+
# +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
|
122
|
+
# 2 rows in set (0.00 sec)
|
123
|
+
#
|
124
|
+
# This is an exercise in Ruby hyperrealism :).
|
125
|
+
def pp(result, elapsed)
|
126
|
+
widths = compute_column_widths(result)
|
127
|
+
separator = build_separator(widths)
|
191
128
|
|
192
|
-
|
193
|
-
QUOTED_FALSE
|
194
|
-
end
|
129
|
+
pp = []
|
195
130
|
|
196
|
-
|
197
|
-
|
198
|
-
|
131
|
+
pp << separator
|
132
|
+
pp << build_cells(result.columns, widths)
|
133
|
+
pp << separator
|
199
134
|
|
200
|
-
|
135
|
+
result.rows.each do |row|
|
136
|
+
pp << build_cells(row, widths)
|
137
|
+
end
|
201
138
|
|
202
|
-
|
203
|
-
|
139
|
+
pp << separator
|
140
|
+
pp << build_footer(result.rows.length, elapsed)
|
204
141
|
|
205
|
-
|
206
|
-
update("SET FOREIGN_KEY_CHECKS = 0")
|
207
|
-
yield
|
208
|
-
ensure
|
209
|
-
update("SET FOREIGN_KEY_CHECKS = #{old}")
|
142
|
+
pp.join("\n") + "\n"
|
210
143
|
end
|
211
|
-
end
|
212
|
-
|
213
|
-
# CONNECTION MANAGEMENT ====================================
|
214
144
|
|
215
|
-
|
216
|
-
return false unless @connection
|
217
|
-
@connection.ping
|
218
|
-
end
|
145
|
+
private
|
219
146
|
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
147
|
+
def compute_column_widths(result)
|
148
|
+
[].tap do |widths|
|
149
|
+
result.columns.each_with_index do |column, i|
|
150
|
+
cells_in_column = [column] + result.rows.map {|r| r[i].nil? ? 'NULL' : r[i].to_s}
|
151
|
+
widths << cells_in_column.map(&:length).max
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
224
155
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
156
|
+
def build_separator(widths)
|
157
|
+
padding = 1
|
158
|
+
'+' + widths.map {|w| '-' * (w + (padding*2))}.join('+') + '+'
|
159
|
+
end
|
229
160
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
161
|
+
def build_cells(items, widths)
|
162
|
+
cells = []
|
163
|
+
items.each_with_index do |item, i|
|
164
|
+
item = 'NULL' if item.nil?
|
165
|
+
justifier = item.is_a?(Numeric) ? 'rjust' : 'ljust'
|
166
|
+
cells << item.to_s.send(justifier, widths[i])
|
167
|
+
end
|
168
|
+
'| ' + cells.join(' | ') + ' |'
|
236
169
|
end
|
237
|
-
end
|
238
170
|
|
239
|
-
|
240
|
-
|
241
|
-
|
171
|
+
def build_footer(nrows, elapsed)
|
172
|
+
rows_label = nrows == 1 ? 'row' : 'rows'
|
173
|
+
"#{nrows} #{rows_label} in set (%.2f sec)" % elapsed
|
174
|
+
end
|
242
175
|
end
|
243
176
|
|
244
|
-
# DATABASE STATEMENTS ======================================
|
245
|
-
|
246
177
|
# FIXME: re-enable the following once a "better" query_cache solution is in core
|
247
178
|
#
|
248
179
|
# The overrides below perform much better than the originals in AbstractAdapter
|
@@ -252,7 +183,7 @@ module ActiveRecord
|
|
252
183
|
# # as values.
|
253
184
|
# def select_one(sql, name = nil)
|
254
185
|
# result = execute(sql, name)
|
255
|
-
# result.each(:
|
186
|
+
# result.each(as: :hash) do |r|
|
256
187
|
# return r
|
257
188
|
# end
|
258
189
|
# end
|
@@ -273,7 +204,7 @@ module ActiveRecord
|
|
273
204
|
|
274
205
|
# Returns an array of arrays containing the field values.
|
275
206
|
# Order is the same as that returned by +columns+.
|
276
|
-
def select_rows(sql, name = nil)
|
207
|
+
def select_rows(sql, name = nil, binds = [])
|
277
208
|
execute(sql, name).to_a
|
278
209
|
end
|
279
210
|
|
@@ -282,26 +213,24 @@ module ActiveRecord
|
|
282
213
|
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
|
283
214
|
# made since we established the connection
|
284
215
|
@connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
|
285
|
-
|
286
|
-
|
287
|
-
else
|
288
|
-
log(sql, name) { @connection.query(sql) }
|
289
|
-
end
|
290
|
-
rescue ActiveRecord::StatementInvalid => exception
|
291
|
-
if exception.message.split(":").first =~ /Packets out of order/
|
292
|
-
raise ActiveRecord::StatementInvalid, "'Packets out of order' error was received from the database. Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information. If you're on Windows, use the Instant Rails installer to get the updated mysql bindings."
|
293
|
-
else
|
294
|
-
raise
|
295
|
-
end
|
216
|
+
|
217
|
+
super
|
296
218
|
end
|
297
219
|
|
220
|
+
def exec_query(sql, name = 'SQL', binds = [])
|
221
|
+
result = execute(sql, name)
|
222
|
+
ActiveRecord::Result.new(result.fields, result.to_a)
|
223
|
+
end
|
224
|
+
|
225
|
+
alias exec_without_stmt exec_query
|
226
|
+
|
298
227
|
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
|
299
228
|
super
|
300
229
|
id_value || @connection.last_id
|
301
230
|
end
|
302
231
|
alias :create :insert_sql
|
303
232
|
|
304
|
-
def exec_insert(sql, name, binds)
|
233
|
+
def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
|
305
234
|
execute to_sql(sql, binds), name
|
306
235
|
end
|
307
236
|
|
@@ -315,416 +244,25 @@ module ActiveRecord
|
|
315
244
|
@connection.last_id
|
316
245
|
end
|
317
246
|
|
318
|
-
|
319
|
-
super
|
320
|
-
@connection.affected_rows
|
321
|
-
end
|
322
|
-
|
323
|
-
def begin_db_transaction
|
324
|
-
execute "BEGIN"
|
325
|
-
rescue Exception
|
326
|
-
# Transactions aren't supported
|
327
|
-
end
|
328
|
-
|
329
|
-
def commit_db_transaction
|
330
|
-
execute "COMMIT"
|
331
|
-
rescue Exception
|
332
|
-
# Transactions aren't supported
|
333
|
-
end
|
334
|
-
|
335
|
-
def rollback_db_transaction
|
336
|
-
execute "ROLLBACK"
|
337
|
-
rescue Exception
|
338
|
-
# Transactions aren't supported
|
339
|
-
end
|
340
|
-
|
341
|
-
def create_savepoint
|
342
|
-
execute("SAVEPOINT #{current_savepoint_name}")
|
343
|
-
end
|
344
|
-
|
345
|
-
def rollback_to_savepoint
|
346
|
-
execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
|
347
|
-
end
|
348
|
-
|
349
|
-
def release_savepoint
|
350
|
-
execute("RELEASE SAVEPOINT #{current_savepoint_name}")
|
351
|
-
end
|
352
|
-
|
353
|
-
def add_limit_offset!(sql, options)
|
354
|
-
limit, offset = options[:limit], options[:offset]
|
355
|
-
if limit && offset
|
356
|
-
sql << " LIMIT #{offset.to_i}, #{sanitize_limit(limit)}"
|
357
|
-
elsif limit
|
358
|
-
sql << " LIMIT #{sanitize_limit(limit)}"
|
359
|
-
elsif offset
|
360
|
-
sql << " OFFSET #{offset.to_i}"
|
361
|
-
end
|
362
|
-
sql
|
363
|
-
end
|
364
|
-
deprecate :add_limit_offset!
|
365
|
-
|
366
|
-
# SCHEMA STATEMENTS ========================================
|
367
|
-
|
368
|
-
def structure_dump
|
369
|
-
if supports_views?
|
370
|
-
sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"
|
371
|
-
else
|
372
|
-
sql = "SHOW TABLES"
|
373
|
-
end
|
374
|
-
|
375
|
-
select_all(sql).inject("") do |structure, table|
|
376
|
-
table.delete('Table_type')
|
377
|
-
structure += select_one("SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}")["Create Table"] + ";\n\n"
|
378
|
-
end
|
379
|
-
end
|
380
|
-
|
381
|
-
# Drops the database specified on the +name+ attribute
|
382
|
-
# and creates it again using the provided +options+.
|
383
|
-
def recreate_database(name, options = {})
|
384
|
-
drop_database(name)
|
385
|
-
create_database(name, options)
|
386
|
-
end
|
387
|
-
|
388
|
-
# Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
|
389
|
-
# Charset defaults to utf8.
|
390
|
-
#
|
391
|
-
# Example:
|
392
|
-
# create_database 'charset_test', :charset => 'latin1', :collation => 'latin1_bin'
|
393
|
-
# create_database 'matt_development'
|
394
|
-
# create_database 'matt_development', :charset => :big5
|
395
|
-
def create_database(name, options = {})
|
396
|
-
if options[:collation]
|
397
|
-
execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}` COLLATE `#{options[:collation]}`"
|
398
|
-
else
|
399
|
-
execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}`"
|
400
|
-
end
|
401
|
-
end
|
402
|
-
|
403
|
-
# Drops a MySQL database.
|
404
|
-
#
|
405
|
-
# Example:
|
406
|
-
# drop_database('sebastian_development')
|
407
|
-
def drop_database(name) #:nodoc:
|
408
|
-
execute "DROP DATABASE IF EXISTS `#{name}`"
|
409
|
-
end
|
410
|
-
|
411
|
-
def current_database
|
412
|
-
select_value 'SELECT DATABASE() as db'
|
413
|
-
end
|
414
|
-
|
415
|
-
# Returns the database character set.
|
416
|
-
def charset
|
417
|
-
show_variable 'character_set_database'
|
418
|
-
end
|
419
|
-
|
420
|
-
# Returns the database collation strategy.
|
421
|
-
def collation
|
422
|
-
show_variable 'collation_database'
|
423
|
-
end
|
424
|
-
|
425
|
-
def tables(name = nil, database = nil) #:nodoc:
|
426
|
-
sql = "SHOW TABLES "
|
427
|
-
sql << "IN #{quote_table_name(database)} " if database
|
428
|
-
|
429
|
-
execute(sql, 'SCHEMA').collect do |field|
|
430
|
-
field.first
|
431
|
-
end
|
432
|
-
end
|
433
|
-
|
434
|
-
def table_exists?(name)
|
435
|
-
return true if super
|
436
|
-
|
437
|
-
name = name.to_s
|
438
|
-
schema, table = name.split('.', 2)
|
439
|
-
|
440
|
-
unless table # A table was provided without a schema
|
441
|
-
table = schema
|
442
|
-
schema = nil
|
443
|
-
end
|
444
|
-
|
445
|
-
tables(nil, schema).include? table
|
446
|
-
end
|
447
|
-
|
448
|
-
def drop_table(table_name, options = {})
|
449
|
-
super(table_name, options)
|
450
|
-
end
|
451
|
-
|
452
|
-
# Returns an array of indexes for the given table.
|
453
|
-
def indexes(table_name, name = nil)
|
454
|
-
indexes = []
|
455
|
-
current_index = nil
|
456
|
-
result = execute("SHOW KEYS FROM #{quote_table_name(table_name)}", 'SCHEMA')
|
457
|
-
result.each(:symbolize_keys => true, :as => :hash) do |row|
|
458
|
-
if current_index != row[:Key_name]
|
459
|
-
next if row[:Key_name] == PRIMARY # skip the primary key
|
460
|
-
current_index = row[:Key_name]
|
461
|
-
indexes << Mysql2IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique] == 0, [], [])
|
462
|
-
end
|
463
|
-
|
464
|
-
indexes.last.columns << row[:Column_name]
|
465
|
-
indexes.last.lengths << row[:Sub_part]
|
466
|
-
end
|
467
|
-
indexes
|
468
|
-
end
|
469
|
-
|
470
|
-
# Returns an array of +Mysql2Column+ objects for the table specified by +table_name+.
|
471
|
-
def columns(table_name, name = nil)
|
472
|
-
sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}"
|
473
|
-
columns = []
|
474
|
-
result = execute(sql, 'SCHEMA')
|
475
|
-
result.each(:symbolize_keys => true, :as => :hash) { |field|
|
476
|
-
columns << Mysql2Column.new(field[:Field], field[:Default], field[:Type], field[:Null] == "YES")
|
477
|
-
}
|
478
|
-
columns
|
479
|
-
end
|
480
|
-
|
481
|
-
def create_table(table_name, options = {})
|
482
|
-
super(table_name, options.reverse_merge(:options => "ENGINE=InnoDB"))
|
483
|
-
end
|
484
|
-
|
485
|
-
# Renames a table.
|
486
|
-
#
|
487
|
-
# Example:
|
488
|
-
# rename_table('octopuses', 'octopi')
|
489
|
-
def rename_table(table_name, new_name)
|
490
|
-
execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
|
491
|
-
end
|
492
|
-
|
493
|
-
def add_column(table_name, column_name, type, options = {})
|
494
|
-
add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
495
|
-
add_column_options!(add_column_sql, options)
|
496
|
-
add_column_position!(add_column_sql, options)
|
497
|
-
execute(add_column_sql)
|
498
|
-
end
|
499
|
-
|
500
|
-
def change_column_default(table_name, column_name, default)
|
501
|
-
column = column_for(table_name, column_name)
|
502
|
-
change_column table_name, column_name, column.sql_type, :default => default
|
503
|
-
end
|
504
|
-
|
505
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
506
|
-
column = column_for(table_name, column_name)
|
507
|
-
|
508
|
-
unless null || default.nil?
|
509
|
-
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
510
|
-
end
|
511
|
-
|
512
|
-
change_column table_name, column_name, column.sql_type, :null => null
|
513
|
-
end
|
514
|
-
|
515
|
-
def change_column(table_name, column_name, type, options = {})
|
516
|
-
column = column_for(table_name, column_name)
|
517
|
-
|
518
|
-
unless options_include_default?(options)
|
519
|
-
options[:default] = column.default
|
520
|
-
end
|
521
|
-
|
522
|
-
unless options.has_key?(:null)
|
523
|
-
options[:null] = column.null
|
524
|
-
end
|
525
|
-
|
526
|
-
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])}"
|
527
|
-
add_column_options!(change_column_sql, options)
|
528
|
-
add_column_position!(change_column_sql, options)
|
529
|
-
execute(change_column_sql)
|
530
|
-
end
|
531
|
-
|
532
|
-
def rename_column(table_name, column_name, new_column_name)
|
533
|
-
options = {}
|
534
|
-
if column = columns(table_name).find { |c| c.name == column_name.to_s }
|
535
|
-
options[:default] = column.default
|
536
|
-
options[:null] = column.null
|
537
|
-
else
|
538
|
-
raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
|
539
|
-
end
|
540
|
-
current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
|
541
|
-
rename_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
|
542
|
-
add_column_options!(rename_column_sql, options)
|
543
|
-
execute(rename_column_sql)
|
544
|
-
end
|
545
|
-
|
546
|
-
# Maps logical Rails types to MySQL-specific data types.
|
547
|
-
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
|
548
|
-
case type.to_s
|
549
|
-
when 'integer'
|
550
|
-
case limit
|
551
|
-
when 1; 'tinyint'
|
552
|
-
when 2; 'smallint'
|
553
|
-
when 3; 'mediumint'
|
554
|
-
when nil, 4, 11; 'int(11)' # compatibility with MySQL default
|
555
|
-
when 5..8; 'bigint'
|
556
|
-
else raise(ActiveRecordError, "No integer type has byte size #{limit}")
|
557
|
-
end
|
558
|
-
when 'text'
|
559
|
-
case limit
|
560
|
-
when 0..0xff; 'tinytext'
|
561
|
-
when nil, 0x100..0xffff; 'text'
|
562
|
-
when 0x10000..0xffffff; 'mediumtext'
|
563
|
-
when 0x1000000..0xffffffff; 'longtext'
|
564
|
-
else raise(ActiveRecordError, "No text type has character length #{limit}")
|
565
|
-
end
|
566
|
-
else
|
567
|
-
super
|
568
|
-
end
|
569
|
-
end
|
570
|
-
|
571
|
-
def add_column_position!(sql, options)
|
572
|
-
if options[:first]
|
573
|
-
sql << " FIRST"
|
574
|
-
elsif options[:after]
|
575
|
-
sql << " AFTER #{quote_column_name(options[:after])}"
|
576
|
-
end
|
577
|
-
end
|
578
|
-
|
579
|
-
# SHOW VARIABLES LIKE 'name'.
|
580
|
-
def show_variable(name)
|
581
|
-
variables = select_all("SHOW VARIABLES LIKE '#{name}'")
|
582
|
-
variables.first['Value'] unless variables.empty?
|
583
|
-
end
|
584
|
-
|
585
|
-
# Returns a table's primary key and belonging sequence.
|
586
|
-
def pk_and_sequence_for(table)
|
587
|
-
result = execute("SHOW CREATE TABLE #{quote_table_name(table)}", 'SCHEMA')
|
588
|
-
create_table = result.first[1]
|
589
|
-
|
590
|
-
if create_table.to_s =~ /PRIMARY KEY\s+\((.+)\)/
|
591
|
-
keys = $1.split(",").map { |key| key.gsub(/[`"]/, "") }
|
592
|
-
keys.length == 1 ? [keys.first, nil] : nil
|
593
|
-
else
|
594
|
-
nil
|
595
|
-
end
|
596
|
-
end
|
597
|
-
|
598
|
-
# Returns just a table's primary key
|
599
|
-
def primary_key(table)
|
600
|
-
pk_and_sequence = pk_and_sequence_for(table)
|
601
|
-
pk_and_sequence && pk_and_sequence.first
|
602
|
-
end
|
247
|
+
private
|
603
248
|
|
604
|
-
def
|
605
|
-
|
249
|
+
def connect
|
250
|
+
@connection = Mysql2::Client.new(@config)
|
251
|
+
configure_connection
|
606
252
|
end
|
607
|
-
deprecate :case_sensitive_equality_operator
|
608
253
|
|
609
|
-
def
|
610
|
-
|
254
|
+
def configure_connection
|
255
|
+
@connection.query_options.merge!(:as => :array)
|
256
|
+
super
|
611
257
|
end
|
612
258
|
|
613
|
-
def
|
614
|
-
|
259
|
+
def full_version
|
260
|
+
@full_version ||= @connection.server_info[:version]
|
615
261
|
end
|
616
262
|
|
617
|
-
|
618
|
-
|
619
|
-
# these, we must use a subquery. However, MySQL is too stupid to create a
|
620
|
-
# temporary table for this automatically, so we have to give it some prompting
|
621
|
-
# in the form of a subsubquery. Ugh!
|
622
|
-
def join_to_update(update, select) #:nodoc:
|
623
|
-
if select.limit || select.offset || select.orders.any?
|
624
|
-
subsubselect = select.clone
|
625
|
-
subsubselect.projections = [update.key]
|
626
|
-
|
627
|
-
subselect = Arel::SelectManager.new(select.engine)
|
628
|
-
subselect.project Arel.sql(update.key.name)
|
629
|
-
subselect.from subsubselect.as('__active_record_temp')
|
630
|
-
|
631
|
-
update.where update.key.in(subselect)
|
632
|
-
else
|
633
|
-
update.table select.source
|
634
|
-
update.wheres = select.constraints
|
635
|
-
end
|
263
|
+
def set_field_encoding field_name
|
264
|
+
field_name
|
636
265
|
end
|
637
|
-
|
638
|
-
protected
|
639
|
-
def quoted_columns_for_index(column_names, options = {})
|
640
|
-
length = options[:length] if options.is_a?(Hash)
|
641
|
-
|
642
|
-
quoted_column_names = case length
|
643
|
-
when Hash
|
644
|
-
column_names.map {|name| length[name] ? "#{quote_column_name(name)}(#{length[name]})" : quote_column_name(name) }
|
645
|
-
when Fixnum
|
646
|
-
column_names.map {|name| "#{quote_column_name(name)}(#{length})"}
|
647
|
-
else
|
648
|
-
column_names.map {|name| quote_column_name(name) }
|
649
|
-
end
|
650
|
-
end
|
651
|
-
|
652
|
-
def translate_exception(exception, message)
|
653
|
-
return super unless exception.respond_to?(:error_number)
|
654
|
-
|
655
|
-
case exception.error_number
|
656
|
-
when 1062
|
657
|
-
RecordNotUnique.new(message, exception)
|
658
|
-
when 1452
|
659
|
-
InvalidForeignKey.new(message, exception)
|
660
|
-
else
|
661
|
-
super
|
662
|
-
end
|
663
|
-
end
|
664
|
-
|
665
|
-
private
|
666
|
-
def connect
|
667
|
-
@connection = Mysql2::Client.new(@config)
|
668
|
-
configure_connection
|
669
|
-
end
|
670
|
-
|
671
|
-
def configure_connection
|
672
|
-
@connection.query_options.merge!(:as => :array)
|
673
|
-
|
674
|
-
# By default, MySQL 'where id is null' selects the last inserted id.
|
675
|
-
# Turn this off. http://dev.rubyonrails.org/ticket/6778
|
676
|
-
variable_assignments = ['SQL_AUTO_IS_NULL=0']
|
677
|
-
encoding = @config[:encoding]
|
678
|
-
|
679
|
-
# make sure we set the encoding
|
680
|
-
variable_assignments << "NAMES '#{encoding}'" if encoding
|
681
|
-
|
682
|
-
# increase timeout so mysql server doesn't disconnect us
|
683
|
-
wait_timeout = @config[:wait_timeout]
|
684
|
-
wait_timeout = 2592000 unless wait_timeout.is_a?(Fixnum)
|
685
|
-
variable_assignments << "@@wait_timeout = #{wait_timeout}"
|
686
|
-
|
687
|
-
execute("SET #{variable_assignments.join(', ')}", :skip_logging)
|
688
|
-
end
|
689
|
-
|
690
|
-
# Returns an array of record hashes with the column names as keys and
|
691
|
-
# column values as values.
|
692
|
-
def select(sql, name = nil, binds = [])
|
693
|
-
exec_query(sql, name).to_a
|
694
|
-
end
|
695
|
-
|
696
|
-
def exec_query(sql, name = 'SQL', binds = [])
|
697
|
-
@connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
|
698
|
-
|
699
|
-
log(sql, name, binds) do
|
700
|
-
begin
|
701
|
-
result = @connection.query(sql)
|
702
|
-
rescue ActiveRecord::StatementInvalid => exception
|
703
|
-
if exception.message.split(":").first =~ /Packets out of order/
|
704
|
-
raise ActiveRecord::StatementInvalid, "'Packets out of order' error was received from the database. Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information. If you're on Windows, use the Instant Rails installer to get the updated mysql bindings."
|
705
|
-
else
|
706
|
-
raise
|
707
|
-
end
|
708
|
-
end
|
709
|
-
|
710
|
-
ActiveRecord::Result.new(result.fields, result.to_a)
|
711
|
-
end
|
712
|
-
end
|
713
|
-
|
714
|
-
def supports_views?
|
715
|
-
version[0] >= 5
|
716
|
-
end
|
717
|
-
|
718
|
-
def version
|
719
|
-
@version ||= @connection.info[:version].scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
|
720
|
-
end
|
721
|
-
|
722
|
-
def column_for(table_name, column_name)
|
723
|
-
unless column = columns(table_name).find { |c| c.name == column_name.to_s }
|
724
|
-
raise "No such column: #{table_name}.#{column_name}"
|
725
|
-
end
|
726
|
-
column
|
727
|
-
end
|
728
266
|
end
|
729
267
|
end
|
730
268
|
end
|