activerecord 3.2.22.5 → 4.2.11.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +1632 -609
- data/MIT-LICENSE +1 -1
- data/README.rdoc +37 -41
- data/examples/performance.rb +31 -19
- data/examples/simple.rb +4 -4
- data/lib/active_record/aggregations.rb +56 -42
- data/lib/active_record/association_relation.rb +35 -0
- data/lib/active_record/associations/alias_tracker.rb +47 -36
- data/lib/active_record/associations/association.rb +73 -55
- data/lib/active_record/associations/association_scope.rb +143 -82
- data/lib/active_record/associations/belongs_to_association.rb +65 -25
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
- data/lib/active_record/associations/builder/association.rb +125 -31
- data/lib/active_record/associations/builder/belongs_to.rb +89 -61
- 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 -51
- data/lib/active_record/associations/builder/singular_association.rb +23 -17
- data/lib/active_record/associations/collection_association.rb +251 -177
- data/lib/active_record/associations/collection_proxy.rb +963 -63
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +113 -22
- data/lib/active_record/associations/has_many_through_association.rb +99 -39
- data/lib/active_record/associations/has_one_association.rb +43 -20
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +76 -107
- 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 +62 -33
- data/lib/active_record/associations/preloader.rb +101 -79
- data/lib/active_record/associations/singular_association.rb +29 -13
- data/lib/active_record/associations/through_association.rb +30 -16
- data/lib/active_record/associations.rb +463 -345
- data/lib/active_record/attribute.rb +163 -0
- data/lib/active_record/attribute_assignment.rb +142 -151
- 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 +137 -57
- data/lib/active_record/attribute_methods/primary_key.rb +50 -36
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +73 -106
- data/lib/active_record/attribute_methods/serialization.rb +44 -94
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +49 -45
- data/lib/active_record/attribute_methods/write.rb +57 -44
- data/lib/active_record/attribute_methods.rb +301 -141
- 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 +246 -217
- data/lib/active_record/base.rb +70 -474
- 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 +396 -219
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -164
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +29 -24
- data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -55
- 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 +261 -169
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +707 -259
- data/lib/active_record/connection_adapters/abstract/transaction.rb +215 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +298 -89
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +466 -196
- data/lib/active_record/connection_adapters/column.rb +31 -245
- data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +45 -57
- data/lib/active_record/connection_adapters/mysql_adapter.rb +180 -123
- 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 +430 -999
- data/lib/active_record/connection_adapters/schema_cache.rb +52 -27
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +579 -22
- data/lib/active_record/connection_handling.rb +132 -0
- data/lib/active_record/core.rb +579 -0
- data/lib/active_record/counter_cache.rb +157 -105
- data/lib/active_record/dynamic_matchers.rb +119 -63
- data/lib/active_record/enum.rb +197 -0
- data/lib/active_record/errors.rb +94 -36
- data/lib/active_record/explain.rb +15 -63
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +9 -5
- data/lib/active_record/fixture_set/file.rb +56 -0
- data/lib/active_record/fixtures.rb +302 -215
- data/lib/active_record/gem_version.rb +15 -0
- data/lib/active_record/inheritance.rb +143 -70
- data/lib/active_record/integration.rb +65 -12
- 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 +73 -52
- data/lib/active_record/locking/pessimistic.rb +5 -5
- data/lib/active_record/log_subscriber.rb +24 -21
- data/lib/active_record/migration/command_recorder.rb +124 -32
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/migration.rb +511 -213
- data/lib/active_record/model_schema.rb +91 -117
- data/lib/active_record/nested_attributes.rb +184 -130
- data/lib/active_record/no_touching.rb +52 -0
- data/lib/active_record/null_relation.rb +81 -0
- data/lib/active_record/persistence.rb +276 -117
- data/lib/active_record/query_cache.rb +19 -37
- data/lib/active_record/querying.rb +28 -18
- data/lib/active_record/railtie.rb +73 -40
- data/lib/active_record/railties/console_sandbox.rb +3 -4
- data/lib/active_record/railties/controller_runtime.rb +4 -3
- data/lib/active_record/railties/databases.rake +141 -416
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +1 -4
- data/lib/active_record/reflection.rb +513 -154
- data/lib/active_record/relation/batches.rb +91 -43
- data/lib/active_record/relation/calculations.rb +199 -161
- data/lib/active_record/relation/delegation.rb +116 -25
- data/lib/active_record/relation/finder_methods.rb +362 -248
- 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 -43
- data/lib/active_record/relation/query_methods.rb +928 -167
- data/lib/active_record/relation/spawn_methods.rb +48 -149
- data/lib/active_record/relation.rb +352 -207
- data/lib/active_record/result.rb +101 -10
- data/lib/active_record/runtime_registry.rb +22 -0
- data/lib/active_record/sanitization.rb +56 -59
- data/lib/active_record/schema.rb +19 -13
- data/lib/active_record/schema_dumper.rb +106 -63
- data/lib/active_record/schema_migration.rb +53 -0
- data/lib/active_record/scoping/default.rb +50 -57
- data/lib/active_record/scoping/named.rb +73 -109
- data/lib/active_record/scoping.rb +58 -123
- data/lib/active_record/serialization.rb +6 -2
- data/lib/active_record/serializers/xml_serializer.rb +12 -22
- data/lib/active_record/statement_cache.rb +111 -0
- data/lib/active_record/store.rb +168 -15
- 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 +23 -16
- data/lib/active_record/transactions.rb +125 -79
- 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 +24 -16
- data/lib/active_record/validations/presence.rb +67 -0
- data/lib/active_record/validations/uniqueness.rb +123 -64
- data/lib/active_record/validations.rb +36 -29
- data/lib/active_record/version.rb +5 -7
- data/lib/active_record.rb +66 -46
- data/lib/rails/generators/active_record/migration/migration_generator.rb +53 -8
- data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +5 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
- data/lib/rails/generators/active_record/migration.rb +11 -8
- data/lib/rails/generators/active_record/model/model_generator.rb +9 -4
- data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- data/lib/rails/generators/active_record.rb +3 -11
- metadata +101 -45
- data/examples/associations.png +0 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
- 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/attribute_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/session_store.rb +0 -360
- data/lib/active_record/test_case.rb +0 -73
- 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 -12
@@ -2,13 +2,11 @@ require 'active_record/connection_adapters/abstract_mysql_adapter'
|
|
2
2
|
require 'active_record/connection_adapters/statement_pool'
|
3
3
|
require 'active_support/core_ext/hash/keys'
|
4
4
|
|
5
|
-
gem 'mysql', '~> 2.
|
5
|
+
gem 'mysql', '~> 2.9'
|
6
6
|
require 'mysql'
|
7
7
|
|
8
8
|
class Mysql
|
9
9
|
class Time
|
10
|
-
###
|
11
|
-
# This monkey patch is for test_additional_columns_from_join_table
|
12
10
|
def to_date
|
13
11
|
Date.new(year, month, day)
|
14
12
|
end
|
@@ -18,9 +16,9 @@ class Mysql
|
|
18
16
|
end
|
19
17
|
|
20
18
|
module ActiveRecord
|
21
|
-
|
19
|
+
module ConnectionHandling # :nodoc:
|
22
20
|
# Establishes a connection to the database that's used by all Active Record objects.
|
23
|
-
def
|
21
|
+
def mysql_connection(config)
|
24
22
|
config = config.symbolize_keys
|
25
23
|
host = config[:host]
|
26
24
|
port = config[:port]
|
@@ -36,6 +34,12 @@ module ActiveRecord
|
|
36
34
|
default_flags |= Mysql::CLIENT_FOUND_ROWS if Mysql.const_defined?(:CLIENT_FOUND_ROWS)
|
37
35
|
options = [host, username, password, database, port, socket, default_flags]
|
38
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
|
39
43
|
end
|
40
44
|
end
|
41
45
|
|
@@ -53,6 +57,8 @@ module ActiveRecord
|
|
53
57
|
# * <tt>:database</tt> - The name of the database. No default, must be provided.
|
54
58
|
# * <tt>:encoding</tt> - (Optional) Sets the client encoding by executing "SET NAMES <encoding>" after connection.
|
55
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).
|
56
62
|
# * <tt>:sslca</tt> - Necessary to use MySQL with an SSL connection.
|
57
63
|
# * <tt>:sslkey</tt> - Necessary to use MySQL with an SSL connection.
|
58
64
|
# * <tt>:sslcert</tt> - Necessary to use MySQL with an SSL connection.
|
@@ -60,36 +66,7 @@ module ActiveRecord
|
|
60
66
|
# * <tt>:sslcipher</tt> - Necessary to use MySQL with an SSL connection.
|
61
67
|
#
|
62
68
|
class MysqlAdapter < AbstractMysqlAdapter
|
63
|
-
|
64
|
-
class Column < AbstractMysqlAdapter::Column #:nodoc:
|
65
|
-
def self.string_to_time(value)
|
66
|
-
return super unless Mysql::Time === value
|
67
|
-
new_time(
|
68
|
-
value.year,
|
69
|
-
value.month,
|
70
|
-
value.day,
|
71
|
-
value.hour,
|
72
|
-
value.minute,
|
73
|
-
value.second,
|
74
|
-
value.second_part)
|
75
|
-
end
|
76
|
-
|
77
|
-
def self.string_to_dummy_time(v)
|
78
|
-
return super unless Mysql::Time === v
|
79
|
-
new_time(2000, 01, 01, v.hour, v.minute, v.second, v.second_part)
|
80
|
-
end
|
81
|
-
|
82
|
-
def self.string_to_date(v)
|
83
|
-
return super unless Mysql::Time === v
|
84
|
-
new_date(v.year, v.month, v.day)
|
85
|
-
end
|
86
|
-
|
87
|
-
def adapter
|
88
|
-
MysqlAdapter
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
ADAPTER_NAME = 'MySQL'
|
69
|
+
ADAPTER_NAME = 'MySQL'.freeze
|
93
70
|
|
94
71
|
class StatementPool < ConnectionAdapters::StatementPool
|
95
72
|
def initialize(connection, max = 1000)
|
@@ -111,7 +88,7 @@ module ActiveRecord
|
|
111
88
|
end
|
112
89
|
|
113
90
|
def clear
|
114
|
-
cache.
|
91
|
+
cache.each_value do |hash|
|
115
92
|
hash[:stmt].close
|
116
93
|
end
|
117
94
|
cache.clear
|
@@ -119,14 +96,14 @@ module ActiveRecord
|
|
119
96
|
|
120
97
|
private
|
121
98
|
def cache
|
122
|
-
@cache[
|
99
|
+
@cache[Process.pid]
|
123
100
|
end
|
124
101
|
end
|
125
102
|
|
126
103
|
def initialize(connection, logger, connection_options, config)
|
127
104
|
super
|
128
105
|
@statements = StatementPool.new(@connection,
|
129
|
-
config.fetch(:statement_limit) { 1000 })
|
106
|
+
self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 }))
|
130
107
|
@client_encoding = nil
|
131
108
|
connect
|
132
109
|
end
|
@@ -150,27 +127,19 @@ module ActiveRecord
|
|
150
127
|
end
|
151
128
|
end
|
152
129
|
|
153
|
-
def new_column(field, default, type, null, collation) # :nodoc:
|
154
|
-
Column.new(field, default, type, null, collation)
|
155
|
-
end
|
156
|
-
|
157
130
|
def error_number(exception) # :nodoc:
|
158
131
|
exception.errno if exception.respond_to?(:errno)
|
159
132
|
end
|
160
133
|
|
161
134
|
# QUOTING ==================================================
|
162
135
|
|
163
|
-
def type_cast(value, column)
|
164
|
-
return super unless value == true || value == false
|
165
|
-
|
166
|
-
value ? 1 : 0
|
167
|
-
end
|
168
|
-
|
169
136
|
def quote_string(string) #:nodoc:
|
170
137
|
@connection.quote(string)
|
171
138
|
end
|
172
139
|
|
140
|
+
#--
|
173
141
|
# CONNECTION MANAGEMENT ====================================
|
142
|
+
#++
|
174
143
|
|
175
144
|
def active?
|
176
145
|
if @connection.respond_to?(:stat)
|
@@ -190,14 +159,15 @@ module ActiveRecord
|
|
190
159
|
end
|
191
160
|
|
192
161
|
def reconnect!
|
162
|
+
super
|
193
163
|
disconnect!
|
194
|
-
clear_cache!
|
195
164
|
connect
|
196
165
|
end
|
197
166
|
|
198
167
|
# Disconnects from the database if already connected. Otherwise, this
|
199
168
|
# method does nothing.
|
200
169
|
def disconnect!
|
170
|
+
super
|
201
171
|
@connection.close rescue nil
|
202
172
|
end
|
203
173
|
|
@@ -210,83 +180,78 @@ module ActiveRecord
|
|
210
180
|
end
|
211
181
|
end
|
212
182
|
|
183
|
+
#--
|
213
184
|
# DATABASE STATEMENTS ======================================
|
185
|
+
#++
|
214
186
|
|
215
|
-
def select_rows(sql, name = nil)
|
187
|
+
def select_rows(sql, name = nil, binds = [])
|
216
188
|
@connection.query_with_result = true
|
217
|
-
rows = exec_query(sql, name).rows
|
189
|
+
rows = exec_query(sql, name, binds).rows
|
218
190
|
@connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
|
219
191
|
rows
|
220
192
|
end
|
221
193
|
|
222
194
|
# Clears the prepared statements cache.
|
223
195
|
def clear_cache!
|
196
|
+
super
|
224
197
|
@statements.clear
|
225
198
|
end
|
226
199
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
}
|
270
|
-
else
|
271
|
-
ENCODINGS = Hash.new { |h,k| h[k] = k }
|
272
|
-
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
|
+
}
|
273
242
|
|
274
243
|
# Get the client encoding for this database
|
275
244
|
def client_encoding
|
276
245
|
return @client_encoding if @client_encoding
|
277
246
|
|
278
247
|
result = exec_query(
|
279
|
-
"
|
248
|
+
"select @@character_set_client",
|
280
249
|
'SCHEMA')
|
281
250
|
@client_encoding = ENCODINGS[result.rows.last.last]
|
282
251
|
end
|
283
252
|
|
284
253
|
def exec_query(sql, name = 'SQL', binds = [])
|
285
|
-
|
286
|
-
# always be empty, since the bind variables will have been already
|
287
|
-
# substituted and removed from binds by BindVisitor, so this will
|
288
|
-
# effectively disable prepared statement usage completely.
|
289
|
-
if binds.empty?
|
254
|
+
if without_prepared_statement?(binds)
|
290
255
|
result_set, affected_rows = exec_without_stmt(sql, name)
|
291
256
|
else
|
292
257
|
result_set, affected_rows = exec_stmt(sql, name, binds)
|
@@ -301,6 +266,79 @@ module ActiveRecord
|
|
301
266
|
@connection.insert_id
|
302
267
|
end
|
303
268
|
|
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
|
285
|
+
|
286
|
+
def has_precision?
|
287
|
+
precision || 0
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
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
|
306
|
+
end
|
307
|
+
|
308
|
+
class << self
|
309
|
+
TYPES = Type::HashLookupTypeMap.new # :nodoc:
|
310
|
+
|
311
|
+
delegate :register_type, :alias_type, to: :TYPES
|
312
|
+
|
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
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
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
|
326
|
+
|
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
|
331
|
+
end
|
332
|
+
|
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)
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
304
342
|
def exec_without_stmt(sql, name = 'SQL') # :nodoc:
|
305
343
|
# Some queries, like SHOW CREATE TABLE don't work through the prepared
|
306
344
|
# statement API. For those queries, we need to use this method. :'(
|
@@ -309,8 +347,20 @@ module ActiveRecord
|
|
309
347
|
affected_rows = @connection.affected_rows
|
310
348
|
|
311
349
|
if result
|
312
|
-
|
313
|
-
|
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)
|
314
364
|
result.free
|
315
365
|
else
|
316
366
|
result_set = ActiveRecord::Result.new([], [])
|
@@ -320,7 +370,7 @@ module ActiveRecord
|
|
320
370
|
end
|
321
371
|
end
|
322
372
|
|
323
|
-
def execute_and_free(sql, name = nil)
|
373
|
+
def execute_and_free(sql, name = nil) # :nodoc:
|
324
374
|
result = execute(sql, name)
|
325
375
|
ret = yield result
|
326
376
|
result.free
|
@@ -333,7 +383,7 @@ module ActiveRecord
|
|
333
383
|
end
|
334
384
|
alias :create :insert_sql
|
335
385
|
|
336
|
-
def exec_delete(sql, name, binds)
|
386
|
+
def exec_delete(sql, name, binds) # :nodoc:
|
337
387
|
affected_rows = 0
|
338
388
|
|
339
389
|
exec_query(sql, name, binds) do |n|
|
@@ -346,15 +396,17 @@ module ActiveRecord
|
|
346
396
|
|
347
397
|
def begin_db_transaction #:nodoc:
|
348
398
|
exec_query "BEGIN"
|
349
|
-
rescue Mysql::Error
|
350
|
-
# Transactions aren't supported
|
351
399
|
end
|
352
400
|
|
353
401
|
private
|
354
402
|
|
355
403
|
def exec_stmt(sql, name, binds)
|
356
404
|
cache = {}
|
357
|
-
|
405
|
+
type_casted_binds = binds.map { |col, val|
|
406
|
+
[col, type_cast(val, col)]
|
407
|
+
}
|
408
|
+
|
409
|
+
log(sql, name, type_casted_binds) do
|
358
410
|
if binds.empty?
|
359
411
|
stmt = @connection.prepare(sql)
|
360
412
|
else
|
@@ -365,10 +417,10 @@ module ActiveRecord
|
|
365
417
|
end
|
366
418
|
|
367
419
|
begin
|
368
|
-
stmt.execute(*
|
420
|
+
stmt.execute(*type_casted_binds.map { |_, val| val })
|
369
421
|
rescue Mysql::Error => e
|
370
422
|
# Older versions of MySQL leave the prepared statement in a bad
|
371
|
-
# place when an error occurs. To support older
|
423
|
+
# place when an error occurs. To support older MySQL versions, we
|
372
424
|
# need to close the statement and delete the statement from the
|
373
425
|
# cache.
|
374
426
|
stmt.close
|
@@ -381,12 +433,12 @@ module ActiveRecord
|
|
381
433
|
cols = cache[:cols] ||= metadata.fetch_fields.map { |field|
|
382
434
|
field.name
|
383
435
|
}
|
436
|
+
metadata.free
|
384
437
|
end
|
385
438
|
|
386
439
|
result_set = ActiveRecord::Result.new(cols, stmt.to_a) if cols
|
387
440
|
affected_rows = stmt.affected_rows
|
388
441
|
|
389
|
-
stmt.result_metadata.free if cols
|
390
442
|
stmt.free_result
|
391
443
|
stmt.close if binds.empty?
|
392
444
|
|
@@ -416,25 +468,30 @@ module ActiveRecord
|
|
416
468
|
configure_connection
|
417
469
|
end
|
418
470
|
|
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.
|
419
473
|
def configure_connection
|
420
|
-
|
421
|
-
execute("SET NAMES '#{encoding}'", :skip_logging) if encoding
|
422
|
-
|
423
|
-
# By default, MySQL 'where id is null' selects the last inserted id.
|
424
|
-
# Turn this off. http://dev.rubyonrails.org/ticket/6778
|
425
|
-
execute("SET SQL_AUTO_IS_NULL=0", :skip_logging)
|
474
|
+
super
|
426
475
|
end
|
427
476
|
|
428
477
|
def select(sql, name = nil, binds = [])
|
429
478
|
@connection.query_with_result = true
|
430
|
-
rows =
|
479
|
+
rows = super
|
431
480
|
@connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
|
432
481
|
rows
|
433
482
|
end
|
434
483
|
|
435
|
-
# Returns the version of the connected MySQL server.
|
436
|
-
def
|
437
|
-
@
|
484
|
+
# Returns the full version of the connected MySQL server.
|
485
|
+
def full_version
|
486
|
+
@full_version ||= @connection.server_info
|
487
|
+
end
|
488
|
+
|
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)
|
493
|
+
end
|
494
|
+
field_name
|
438
495
|
end
|
439
496
|
end
|
440
497
|
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module PostgreSQL
|
4
|
+
module ArrayParser # :nodoc:
|
5
|
+
|
6
|
+
DOUBLE_QUOTE = '"'
|
7
|
+
BACKSLASH = "\\"
|
8
|
+
COMMA = ','
|
9
|
+
BRACKET_OPEN = '{'
|
10
|
+
BRACKET_CLOSE = '}'
|
11
|
+
|
12
|
+
def parse_pg_array(string) # :nodoc:
|
13
|
+
local_index = 0
|
14
|
+
array = []
|
15
|
+
while(local_index < string.length)
|
16
|
+
case string[local_index]
|
17
|
+
when BRACKET_OPEN
|
18
|
+
local_index,array = parse_array_contents(array, string, local_index + 1)
|
19
|
+
when BRACKET_CLOSE
|
20
|
+
return array
|
21
|
+
end
|
22
|
+
local_index += 1
|
23
|
+
end
|
24
|
+
|
25
|
+
array
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def parse_array_contents(array, string, index)
|
31
|
+
is_escaping = false
|
32
|
+
is_quoted = false
|
33
|
+
was_quoted = false
|
34
|
+
current_item = ''
|
35
|
+
|
36
|
+
local_index = index
|
37
|
+
while local_index
|
38
|
+
token = string[local_index]
|
39
|
+
if is_escaping
|
40
|
+
current_item << token
|
41
|
+
is_escaping = false
|
42
|
+
else
|
43
|
+
if is_quoted
|
44
|
+
case token
|
45
|
+
when DOUBLE_QUOTE
|
46
|
+
is_quoted = false
|
47
|
+
was_quoted = true
|
48
|
+
when BACKSLASH
|
49
|
+
is_escaping = true
|
50
|
+
else
|
51
|
+
current_item << token
|
52
|
+
end
|
53
|
+
else
|
54
|
+
case token
|
55
|
+
when BACKSLASH
|
56
|
+
is_escaping = true
|
57
|
+
when COMMA
|
58
|
+
add_item_to_array(array, current_item, was_quoted)
|
59
|
+
current_item = ''
|
60
|
+
was_quoted = false
|
61
|
+
when DOUBLE_QUOTE
|
62
|
+
is_quoted = true
|
63
|
+
when BRACKET_OPEN
|
64
|
+
internal_items = []
|
65
|
+
local_index,internal_items = parse_array_contents(internal_items, string, local_index + 1)
|
66
|
+
array.push(internal_items)
|
67
|
+
when BRACKET_CLOSE
|
68
|
+
add_item_to_array(array, current_item, was_quoted)
|
69
|
+
return local_index,array
|
70
|
+
else
|
71
|
+
current_item << token
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
local_index += 1
|
77
|
+
end
|
78
|
+
return local_index,array
|
79
|
+
end
|
80
|
+
|
81
|
+
def add_item_to_array(array, current_item, quoted)
|
82
|
+
return if !quoted && current_item.length == 0
|
83
|
+
|
84
|
+
if !quoted && current_item == 'NULL'
|
85
|
+
array.push nil
|
86
|
+
else
|
87
|
+
array.push current_item
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
# PostgreSQL-specific extensions to column definitions in a table.
|
4
|
+
class PostgreSQLColumn < Column #:nodoc:
|
5
|
+
attr_accessor :array
|
6
|
+
|
7
|
+
def initialize(name, default, cast_type, sql_type = nil, null = true, default_function = nil)
|
8
|
+
if sql_type =~ /\[\]$/
|
9
|
+
@array = true
|
10
|
+
super(name, default, cast_type, sql_type[0..sql_type.length - 3], null)
|
11
|
+
else
|
12
|
+
@array = false
|
13
|
+
super(name, default, cast_type, sql_type, null)
|
14
|
+
end
|
15
|
+
|
16
|
+
@default_function = default_function
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|