activerecord 4.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (221) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1372 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +218 -0
  5. data/examples/performance.rb +184 -0
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record.rb +173 -0
  8. data/lib/active_record/aggregations.rb +266 -0
  9. data/lib/active_record/association_relation.rb +22 -0
  10. data/lib/active_record/associations.rb +1724 -0
  11. data/lib/active_record/associations/alias_tracker.rb +87 -0
  12. data/lib/active_record/associations/association.rb +253 -0
  13. data/lib/active_record/associations/association_scope.rb +194 -0
  14. data/lib/active_record/associations/belongs_to_association.rb +111 -0
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +40 -0
  16. data/lib/active_record/associations/builder/association.rb +149 -0
  17. data/lib/active_record/associations/builder/belongs_to.rb +116 -0
  18. data/lib/active_record/associations/builder/collection_association.rb +91 -0
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +124 -0
  20. data/lib/active_record/associations/builder/has_many.rb +15 -0
  21. data/lib/active_record/associations/builder/has_one.rb +23 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +38 -0
  23. data/lib/active_record/associations/collection_association.rb +634 -0
  24. data/lib/active_record/associations/collection_proxy.rb +1027 -0
  25. data/lib/active_record/associations/has_many_association.rb +184 -0
  26. data/lib/active_record/associations/has_many_through_association.rb +238 -0
  27. data/lib/active_record/associations/has_one_association.rb +105 -0
  28. data/lib/active_record/associations/has_one_through_association.rb +36 -0
  29. data/lib/active_record/associations/join_dependency.rb +282 -0
  30. data/lib/active_record/associations/join_dependency/join_association.rb +122 -0
  31. data/lib/active_record/associations/join_dependency/join_base.rb +22 -0
  32. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  33. data/lib/active_record/associations/preloader.rb +203 -0
  34. data/lib/active_record/associations/preloader/association.rb +162 -0
  35. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  36. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  37. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  38. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  39. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  40. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  41. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  42. data/lib/active_record/associations/preloader/through_association.rb +96 -0
  43. data/lib/active_record/associations/singular_association.rb +86 -0
  44. data/lib/active_record/associations/through_association.rb +96 -0
  45. data/lib/active_record/attribute.rb +149 -0
  46. data/lib/active_record/attribute_assignment.rb +212 -0
  47. data/lib/active_record/attribute_decorators.rb +66 -0
  48. data/lib/active_record/attribute_methods.rb +439 -0
  49. data/lib/active_record/attribute_methods/before_type_cast.rb +71 -0
  50. data/lib/active_record/attribute_methods/dirty.rb +181 -0
  51. data/lib/active_record/attribute_methods/primary_key.rb +128 -0
  52. data/lib/active_record/attribute_methods/query.rb +40 -0
  53. data/lib/active_record/attribute_methods/read.rb +103 -0
  54. data/lib/active_record/attribute_methods/serialization.rb +70 -0
  55. data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -0
  56. data/lib/active_record/attribute_methods/write.rb +83 -0
  57. data/lib/active_record/attribute_set.rb +77 -0
  58. data/lib/active_record/attribute_set/builder.rb +86 -0
  59. data/lib/active_record/attributes.rb +139 -0
  60. data/lib/active_record/autosave_association.rb +439 -0
  61. data/lib/active_record/base.rb +317 -0
  62. data/lib/active_record/callbacks.rb +313 -0
  63. data/lib/active_record/coders/json.rb +13 -0
  64. data/lib/active_record/coders/yaml_column.rb +38 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +659 -0
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +373 -0
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +133 -0
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +574 -0
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +991 -0
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +219 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +487 -0
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +883 -0
  78. data/lib/active_record/connection_adapters/column.rb +82 -0
  79. data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
  80. data/lib/active_record/connection_adapters/mysql2_adapter.rb +282 -0
  81. data/lib/active_record/connection_adapters/mysql_adapter.rb +491 -0
  82. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
  83. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  84. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  111. data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
  112. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  114. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +588 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +754 -0
  117. data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +628 -0
  119. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  120. data/lib/active_record/connection_handling.rb +132 -0
  121. data/lib/active_record/core.rb +566 -0
  122. data/lib/active_record/counter_cache.rb +175 -0
  123. data/lib/active_record/dynamic_matchers.rb +140 -0
  124. data/lib/active_record/enum.rb +198 -0
  125. data/lib/active_record/errors.rb +252 -0
  126. data/lib/active_record/explain.rb +38 -0
  127. data/lib/active_record/explain_registry.rb +30 -0
  128. data/lib/active_record/explain_subscriber.rb +29 -0
  129. data/lib/active_record/fixture_set/file.rb +56 -0
  130. data/lib/active_record/fixtures.rb +1007 -0
  131. data/lib/active_record/gem_version.rb +15 -0
  132. data/lib/active_record/inheritance.rb +247 -0
  133. data/lib/active_record/integration.rb +113 -0
  134. data/lib/active_record/locale/en.yml +47 -0
  135. data/lib/active_record/locking/optimistic.rb +204 -0
  136. data/lib/active_record/locking/pessimistic.rb +77 -0
  137. data/lib/active_record/log_subscriber.rb +75 -0
  138. data/lib/active_record/migration.rb +1051 -0
  139. data/lib/active_record/migration/command_recorder.rb +197 -0
  140. data/lib/active_record/migration/join_table.rb +15 -0
  141. data/lib/active_record/model_schema.rb +340 -0
  142. data/lib/active_record/nested_attributes.rb +548 -0
  143. data/lib/active_record/no_touching.rb +52 -0
  144. data/lib/active_record/null_relation.rb +81 -0
  145. data/lib/active_record/persistence.rb +532 -0
  146. data/lib/active_record/query_cache.rb +56 -0
  147. data/lib/active_record/querying.rb +68 -0
  148. data/lib/active_record/railtie.rb +162 -0
  149. data/lib/active_record/railties/console_sandbox.rb +5 -0
  150. data/lib/active_record/railties/controller_runtime.rb +50 -0
  151. data/lib/active_record/railties/databases.rake +391 -0
  152. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  153. data/lib/active_record/readonly_attributes.rb +23 -0
  154. data/lib/active_record/reflection.rb +881 -0
  155. data/lib/active_record/relation.rb +681 -0
  156. data/lib/active_record/relation/batches.rb +138 -0
  157. data/lib/active_record/relation/calculations.rb +403 -0
  158. data/lib/active_record/relation/delegation.rb +140 -0
  159. data/lib/active_record/relation/finder_methods.rb +528 -0
  160. data/lib/active_record/relation/merger.rb +170 -0
  161. data/lib/active_record/relation/predicate_builder.rb +126 -0
  162. data/lib/active_record/relation/predicate_builder/array_handler.rb +47 -0
  163. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  164. data/lib/active_record/relation/query_methods.rb +1176 -0
  165. data/lib/active_record/relation/spawn_methods.rb +75 -0
  166. data/lib/active_record/result.rb +131 -0
  167. data/lib/active_record/runtime_registry.rb +22 -0
  168. data/lib/active_record/sanitization.rb +191 -0
  169. data/lib/active_record/schema.rb +64 -0
  170. data/lib/active_record/schema_dumper.rb +251 -0
  171. data/lib/active_record/schema_migration.rb +56 -0
  172. data/lib/active_record/scoping.rb +87 -0
  173. data/lib/active_record/scoping/default.rb +134 -0
  174. data/lib/active_record/scoping/named.rb +164 -0
  175. data/lib/active_record/serialization.rb +22 -0
  176. data/lib/active_record/serializers/xml_serializer.rb +193 -0
  177. data/lib/active_record/statement_cache.rb +111 -0
  178. data/lib/active_record/store.rb +205 -0
  179. data/lib/active_record/tasks/database_tasks.rb +296 -0
  180. data/lib/active_record/tasks/mysql_database_tasks.rb +145 -0
  181. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  182. data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
  183. data/lib/active_record/timestamp.rb +121 -0
  184. data/lib/active_record/transactions.rb +417 -0
  185. data/lib/active_record/translation.rb +22 -0
  186. data/lib/active_record/type.rb +23 -0
  187. data/lib/active_record/type/big_integer.rb +13 -0
  188. data/lib/active_record/type/binary.rb +50 -0
  189. data/lib/active_record/type/boolean.rb +30 -0
  190. data/lib/active_record/type/date.rb +46 -0
  191. data/lib/active_record/type/date_time.rb +43 -0
  192. data/lib/active_record/type/decimal.rb +40 -0
  193. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  194. data/lib/active_record/type/decorator.rb +14 -0
  195. data/lib/active_record/type/float.rb +19 -0
  196. data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
  197. data/lib/active_record/type/integer.rb +55 -0
  198. data/lib/active_record/type/mutable.rb +16 -0
  199. data/lib/active_record/type/numeric.rb +36 -0
  200. data/lib/active_record/type/serialized.rb +56 -0
  201. data/lib/active_record/type/string.rb +36 -0
  202. data/lib/active_record/type/text.rb +11 -0
  203. data/lib/active_record/type/time.rb +26 -0
  204. data/lib/active_record/type/time_value.rb +38 -0
  205. data/lib/active_record/type/type_map.rb +64 -0
  206. data/lib/active_record/type/unsigned_integer.rb +15 -0
  207. data/lib/active_record/type/value.rb +101 -0
  208. data/lib/active_record/validations.rb +90 -0
  209. data/lib/active_record/validations/associated.rb +51 -0
  210. data/lib/active_record/validations/presence.rb +67 -0
  211. data/lib/active_record/validations/uniqueness.rb +229 -0
  212. data/lib/active_record/version.rb +8 -0
  213. data/lib/rails/generators/active_record.rb +17 -0
  214. data/lib/rails/generators/active_record/migration.rb +18 -0
  215. data/lib/rails/generators/active_record/migration/migration_generator.rb +70 -0
  216. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +22 -0
  217. data/lib/rails/generators/active_record/migration/templates/migration.rb +45 -0
  218. data/lib/rails/generators/active_record/model/model_generator.rb +52 -0
  219. data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
  220. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  221. metadata +309 -0
@@ -0,0 +1,82 @@
1
+ require 'set'
2
+
3
+ module ActiveRecord
4
+ # :stopdoc:
5
+ module ConnectionAdapters
6
+ # An abstract definition of a column in a table.
7
+ class Column
8
+ TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON'].to_set
9
+ FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE', 'off', 'OFF'].to_set
10
+
11
+ module Format
12
+ ISO_DATE = /\A(\d{4})-(\d\d)-(\d\d)\z/
13
+ ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
14
+ end
15
+
16
+ attr_reader :name, :cast_type, :null, :sql_type, :default, :default_function
17
+
18
+ delegate :type, :precision, :scale, :limit, :klass, :accessor,
19
+ :number?, :binary?, :changed?,
20
+ :type_cast_from_user, :type_cast_from_database, :type_cast_for_database,
21
+ :type_cast_for_schema,
22
+ to: :cast_type
23
+
24
+ # Instantiates a new column in the table.
25
+ #
26
+ # +name+ is the column's name, such as <tt>supplier_id</tt> in <tt>supplier_id int(11)</tt>.
27
+ # +default+ is the type-casted default value, such as +new+ in <tt>sales_stage varchar(20) default 'new'</tt>.
28
+ # +cast_type+ is the object used for type casting and type information.
29
+ # +sql_type+ is used to extract the column's length, if necessary. For example +60+ in
30
+ # <tt>company_name varchar(60)</tt>.
31
+ # It will be mapped to one of the standard Rails SQL types in the <tt>type</tt> attribute.
32
+ # +null+ determines if this column allows +NULL+ values.
33
+ def initialize(name, default, cast_type, sql_type = nil, null = true)
34
+ @name = name
35
+ @cast_type = cast_type
36
+ @sql_type = sql_type
37
+ @null = null
38
+ @default = default
39
+ @default_function = nil
40
+ end
41
+
42
+ def has_default?
43
+ !default.nil?
44
+ end
45
+
46
+ # Returns the human name of the column name.
47
+ #
48
+ # ===== Examples
49
+ # Column.new('sales_stage', ...).human_name # => 'Sales stage'
50
+ def human_name
51
+ Base.human_attribute_name(@name)
52
+ end
53
+
54
+ def with_type(type)
55
+ dup.tap do |clone|
56
+ clone.instance_variable_set('@cast_type', type)
57
+ end
58
+ end
59
+
60
+ def ==(other)
61
+ other.name == name &&
62
+ other.default == default &&
63
+ other.cast_type == cast_type &&
64
+ other.sql_type == sql_type &&
65
+ other.null == null &&
66
+ other.default_function == default_function
67
+ end
68
+ alias :eql? :==
69
+
70
+ def hash
71
+ attributes_for_hash.hash
72
+ end
73
+
74
+ private
75
+
76
+ def attributes_for_hash
77
+ [self.class, name, default, cast_type, sql_type, null, default_function]
78
+ end
79
+ end
80
+ end
81
+ # :startdoc:
82
+ end
@@ -0,0 +1,275 @@
1
+ require 'uri'
2
+ require 'active_support/core_ext/string/filters'
3
+
4
+ module ActiveRecord
5
+ module ConnectionAdapters
6
+ class ConnectionSpecification #:nodoc:
7
+ attr_reader :config, :adapter_method
8
+
9
+ def initialize(config, adapter_method)
10
+ @config, @adapter_method = config, adapter_method
11
+ end
12
+
13
+ def initialize_dup(original)
14
+ @config = original.config.dup
15
+ end
16
+
17
+ # Expands a connection string into a hash.
18
+ class ConnectionUrlResolver # :nodoc:
19
+
20
+ # == Example
21
+ #
22
+ # url = "postgresql://foo:bar@localhost:9000/foo_test?pool=5&timeout=3000"
23
+ # ConnectionUrlResolver.new(url).to_hash
24
+ # # => {
25
+ # "adapter" => "postgresql",
26
+ # "host" => "localhost",
27
+ # "port" => 9000,
28
+ # "database" => "foo_test",
29
+ # "username" => "foo",
30
+ # "password" => "bar",
31
+ # "pool" => "5",
32
+ # "timeout" => "3000"
33
+ # }
34
+ def initialize(url)
35
+ raise "Database URL cannot be empty" if url.blank?
36
+ @uri = uri_parser.parse(url)
37
+ @adapter = @uri.scheme.tr('-', '_')
38
+ @adapter = "postgresql" if @adapter == "postgres"
39
+
40
+ if @uri.opaque
41
+ @uri.opaque, @query = @uri.opaque.split('?', 2)
42
+ else
43
+ @query = @uri.query
44
+ end
45
+ end
46
+
47
+ # Converts the given URL to a full connection hash.
48
+ def to_hash
49
+ config = raw_config.reject { |_,value| value.blank? }
50
+ config.map { |key,value| config[key] = uri_parser.unescape(value) if value.is_a? String }
51
+ config
52
+ end
53
+
54
+ private
55
+
56
+ def uri
57
+ @uri
58
+ end
59
+
60
+ def uri_parser
61
+ @uri_parser ||= URI::Parser.new
62
+ end
63
+
64
+ # Converts the query parameters of the URI into a hash.
65
+ #
66
+ # "localhost?pool=5&reaping_frequency=2"
67
+ # # => { "pool" => "5", "reaping_frequency" => "2" }
68
+ #
69
+ # returns empty hash if no query present.
70
+ #
71
+ # "localhost"
72
+ # # => {}
73
+ def query_hash
74
+ Hash[(@query || '').split("&").map { |pair| pair.split("=") }]
75
+ end
76
+
77
+ def raw_config
78
+ if uri.opaque
79
+ query_hash.merge({
80
+ "adapter" => @adapter,
81
+ "database" => uri.opaque })
82
+ else
83
+ query_hash.merge({
84
+ "adapter" => @adapter,
85
+ "username" => uri.user,
86
+ "password" => uri.password,
87
+ "port" => uri.port,
88
+ "database" => database_from_path,
89
+ "host" => uri.hostname })
90
+ end
91
+ end
92
+
93
+ # Returns name of the database.
94
+ def database_from_path
95
+ if @adapter == 'sqlite3'
96
+ # 'sqlite3:/foo' is absolute, because that makes sense. The
97
+ # corresponding relative version, 'sqlite3:foo', is handled
98
+ # elsewhere, as an "opaque".
99
+
100
+ uri.path
101
+ else
102
+ # Only SQLite uses a filename as the "database" name; for
103
+ # anything else, a leading slash would be silly.
104
+
105
+ uri.path.sub(%r{^/}, "")
106
+ end
107
+ end
108
+ end
109
+
110
+ ##
111
+ # Builds a ConnectionSpecification from user input.
112
+ class Resolver # :nodoc:
113
+ attr_reader :configurations
114
+
115
+ # Accepts a hash two layers deep, keys on the first layer represent
116
+ # environments such as "production". Keys must be strings.
117
+ def initialize(configurations)
118
+ @configurations = configurations
119
+ end
120
+
121
+ # Returns a hash with database connection information.
122
+ #
123
+ # == Examples
124
+ #
125
+ # Full hash Configuration.
126
+ #
127
+ # configurations = { "production" => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" } }
128
+ # Resolver.new(configurations).resolve(:production)
129
+ # # => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3"}
130
+ #
131
+ # Initialized with URL configuration strings.
132
+ #
133
+ # configurations = { "production" => "postgresql://localhost/foo" }
134
+ # Resolver.new(configurations).resolve(:production)
135
+ # # => { "host" => "localhost", "database" => "foo", "adapter" => "postgresql" }
136
+ #
137
+ def resolve(config)
138
+ if config
139
+ resolve_connection config
140
+ elsif env = ActiveRecord::ConnectionHandling::RAILS_ENV.call
141
+ resolve_symbol_connection env.to_sym
142
+ else
143
+ raise AdapterNotSpecified
144
+ end
145
+ end
146
+
147
+ # Expands each key in @configurations hash into fully resolved hash
148
+ def resolve_all
149
+ config = configurations.dup
150
+ config.each do |key, value|
151
+ config[key] = resolve(value) if value
152
+ end
153
+ config
154
+ end
155
+
156
+ # Returns an instance of ConnectionSpecification for a given adapter.
157
+ # Accepts a hash one layer deep that contains all connection information.
158
+ #
159
+ # == Example
160
+ #
161
+ # config = { "production" => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" } }
162
+ # spec = Resolver.new(config).spec(:production)
163
+ # spec.adapter_method
164
+ # # => "sqlite3_connection"
165
+ # spec.config
166
+ # # => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" }
167
+ #
168
+ def spec(config)
169
+ spec = resolve(config).symbolize_keys
170
+
171
+ raise(AdapterNotSpecified, "database configuration does not specify adapter") unless spec.key?(:adapter)
172
+
173
+ path_to_adapter = "active_record/connection_adapters/#{spec[:adapter]}_adapter"
174
+ begin
175
+ require path_to_adapter
176
+ rescue Gem::LoadError => e
177
+ raise Gem::LoadError, "Specified '#{spec[:adapter]}' for database adapter, but the gem is not loaded. Add `gem '#{e.name}'` to your Gemfile (and ensure its version is at the minimum required by ActiveRecord)."
178
+ rescue LoadError => e
179
+ raise LoadError, "Could not load '#{path_to_adapter}'. Make sure that the adapter in config/database.yml is valid. If you use an adapter other than 'mysql', 'mysql2', 'postgresql' or 'sqlite3' add the necessary adapter gem to the Gemfile.", e.backtrace
180
+ end
181
+
182
+ adapter_method = "#{spec[:adapter]}_connection"
183
+ ConnectionSpecification.new(spec, adapter_method)
184
+ end
185
+
186
+ private
187
+
188
+ # Returns fully resolved connection, accepts hash, string or symbol.
189
+ # Always returns a hash.
190
+ #
191
+ # == Examples
192
+ #
193
+ # Symbol representing current environment.
194
+ #
195
+ # Resolver.new("production" => {}).resolve_connection(:production)
196
+ # # => {}
197
+ #
198
+ # One layer deep hash of connection values.
199
+ #
200
+ # Resolver.new({}).resolve_connection("adapter" => "sqlite3")
201
+ # # => { "adapter" => "sqlite3" }
202
+ #
203
+ # Connection URL.
204
+ #
205
+ # Resolver.new({}).resolve_connection("postgresql://localhost/foo")
206
+ # # => { "host" => "localhost", "database" => "foo", "adapter" => "postgresql" }
207
+ #
208
+ def resolve_connection(spec)
209
+ case spec
210
+ when Symbol
211
+ resolve_symbol_connection spec
212
+ when String
213
+ resolve_string_connection spec
214
+ when Hash
215
+ resolve_hash_connection spec
216
+ end
217
+ end
218
+
219
+ def resolve_string_connection(spec)
220
+ # Rails has historically accepted a string to mean either
221
+ # an environment key or a URL spec, so we have deprecated
222
+ # this ambiguous behaviour and in the future this function
223
+ # can be removed in favor of resolve_url_connection.
224
+ if configurations.key?(spec) || spec !~ /:/
225
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
226
+ Passing a string to ActiveRecord::Base.establish_connection for a
227
+ configuration lookup is deprecated, please pass a symbol
228
+ (#{spec.to_sym.inspect}) instead.
229
+ MSG
230
+
231
+ resolve_symbol_connection(spec)
232
+ else
233
+ resolve_url_connection(spec)
234
+ end
235
+ end
236
+
237
+ # Takes the environment such as +:production+ or +:development+.
238
+ # This requires that the @configurations was initialized with a key that
239
+ # matches.
240
+ #
241
+ # Resolver.new("production" => {}).resolve_symbol_connection(:production)
242
+ # # => {}
243
+ #
244
+ def resolve_symbol_connection(spec)
245
+ if config = configurations[spec.to_s]
246
+ resolve_connection(config)
247
+ else
248
+ raise(AdapterNotSpecified, "'#{spec}' database is not configured. Available: #{configurations.keys.inspect}")
249
+ end
250
+ end
251
+
252
+ # Accepts a hash. Expands the "url" key that contains a
253
+ # URL database connection to a full connection
254
+ # hash and merges with the rest of the hash.
255
+ # Connection details inside of the "url" key win any merge conflicts
256
+ def resolve_hash_connection(spec)
257
+ if spec["url"] && spec["url"] !~ /^jdbc:/
258
+ connection_hash = resolve_url_connection(spec.delete("url"))
259
+ spec.merge!(connection_hash)
260
+ end
261
+ spec
262
+ end
263
+
264
+ # Takes a connection URL.
265
+ #
266
+ # Resolver.new({}).resolve_url_connection("postgresql://localhost/foo")
267
+ # # => { "host" => "localhost", "database" => "foo", "adapter" => "postgresql" }
268
+ #
269
+ def resolve_url_connection(url)
270
+ ConnectionUrlResolver.new(url).to_hash
271
+ end
272
+ end
273
+ end
274
+ end
275
+ end
@@ -0,0 +1,282 @@
1
+ require 'active_record/connection_adapters/abstract_mysql_adapter'
2
+
3
+ gem 'mysql2', '~> 0.3.13'
4
+ require 'mysql2'
5
+
6
+ module ActiveRecord
7
+ module ConnectionHandling # :nodoc:
8
+ # Establishes a connection to the database that's used by all Active Record objects.
9
+ def mysql2_connection(config)
10
+ config = config.symbolize_keys
11
+
12
+ config[:username] = 'root' if config[:username].nil?
13
+
14
+ if Mysql2::Client.const_defined? :FOUND_ROWS
15
+ config[:flags] = Mysql2::Client::FOUND_ROWS
16
+ end
17
+
18
+ client = Mysql2::Client.new(config)
19
+ options = [config[:host], config[:username], config[:password], config[:database], config[:port], config[:socket], 0]
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
27
+ end
28
+ end
29
+
30
+ module ConnectionAdapters
31
+ class Mysql2Adapter < AbstractMysqlAdapter
32
+ ADAPTER_NAME = 'Mysql2'.freeze
33
+
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 @config[:encoding] == 'utf8mb4'
43
+ ActiveRecord::SchemaMigration.create_table(MAX_INDEX_LENGTH_FOR_UTF8MB4)
44
+ else
45
+ ActiveRecord::SchemaMigration.create_table
46
+ end
47
+ end
48
+
49
+ def supports_explain?
50
+ true
51
+ end
52
+
53
+ # HELPER METHODS ===========================================
54
+
55
+ def each_hash(result) # :nodoc:
56
+ if block_given?
57
+ result.each(:as => :hash, :symbolize_keys => true) do |row|
58
+ yield row
59
+ end
60
+ else
61
+ to_enum(:each_hash, result)
62
+ end
63
+ end
64
+
65
+ def error_number(exception)
66
+ exception.error_number if exception.respond_to?(:error_number)
67
+ end
68
+
69
+ #--
70
+ # QUOTING ==================================================
71
+ #++
72
+
73
+ def quote_string(string)
74
+ @connection.escape(string)
75
+ end
76
+
77
+ def quoted_date(value)
78
+ if value.acts_like?(:time) && value.respond_to?(:usec)
79
+ "#{super}.#{sprintf("%06d", value.usec)}"
80
+ else
81
+ super
82
+ end
83
+ end
84
+
85
+ #--
86
+ # CONNECTION MANAGEMENT ====================================
87
+ #++
88
+
89
+ def active?
90
+ return false unless @connection
91
+ @connection.ping
92
+ end
93
+
94
+ def reconnect!
95
+ super
96
+ disconnect!
97
+ connect
98
+ end
99
+ alias :reset! :reconnect!
100
+
101
+ # Disconnects from the database if already connected.
102
+ # Otherwise, this method does nothing.
103
+ def disconnect!
104
+ super
105
+ unless @connection.nil?
106
+ @connection.close
107
+ @connection = nil
108
+ end
109
+ end
110
+
111
+ #--
112
+ # DATABASE STATEMENTS ======================================
113
+ #++
114
+
115
+ def explain(arel, binds = [])
116
+ sql = "EXPLAIN #{to_sql(arel, binds.dup)}"
117
+ start = Time.now
118
+ result = exec_query(sql, 'EXPLAIN', binds)
119
+ elapsed = Time.now - start
120
+
121
+ ExplainPrettyPrinter.new.pp(result, elapsed)
122
+ end
123
+
124
+ class ExplainPrettyPrinter # :nodoc:
125
+ # Pretty prints the result of a EXPLAIN in a way that resembles the output of the
126
+ # MySQL shell:
127
+ #
128
+ # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
129
+ # | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
130
+ # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
131
+ # | 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | |
132
+ # | 1 | SIMPLE | posts | ALL | NULL | NULL | NULL | NULL | 1 | Using where |
133
+ # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
134
+ # 2 rows in set (0.00 sec)
135
+ #
136
+ # This is an exercise in Ruby hyperrealism :).
137
+ def pp(result, elapsed)
138
+ widths = compute_column_widths(result)
139
+ separator = build_separator(widths)
140
+
141
+ pp = []
142
+
143
+ pp << separator
144
+ pp << build_cells(result.columns, widths)
145
+ pp << separator
146
+
147
+ result.rows.each do |row|
148
+ pp << build_cells(row, widths)
149
+ end
150
+
151
+ pp << separator
152
+ pp << build_footer(result.rows.length, elapsed)
153
+
154
+ pp.join("\n") + "\n"
155
+ end
156
+
157
+ private
158
+
159
+ def compute_column_widths(result)
160
+ [].tap do |widths|
161
+ result.columns.each_with_index do |column, i|
162
+ cells_in_column = [column] + result.rows.map {|r| r[i].nil? ? 'NULL' : r[i].to_s}
163
+ widths << cells_in_column.map(&:length).max
164
+ end
165
+ end
166
+ end
167
+
168
+ def build_separator(widths)
169
+ padding = 1
170
+ '+' + widths.map {|w| '-' * (w + (padding*2))}.join('+') + '+'
171
+ end
172
+
173
+ def build_cells(items, widths)
174
+ cells = []
175
+ items.each_with_index do |item, i|
176
+ item = 'NULL' if item.nil?
177
+ justifier = item.is_a?(Numeric) ? 'rjust' : 'ljust'
178
+ cells << item.to_s.send(justifier, widths[i])
179
+ end
180
+ '| ' + cells.join(' | ') + ' |'
181
+ end
182
+
183
+ def build_footer(nrows, elapsed)
184
+ rows_label = nrows == 1 ? 'row' : 'rows'
185
+ "#{nrows} #{rows_label} in set (%.2f sec)" % elapsed
186
+ end
187
+ end
188
+
189
+ # FIXME: re-enable the following once a "better" query_cache solution is in core
190
+ #
191
+ # The overrides below perform much better than the originals in AbstractAdapter
192
+ # because we're able to take advantage of mysql2's lazy-loading capabilities
193
+ #
194
+ # # Returns a record hash with the column names as keys and column values
195
+ # # as values.
196
+ # def select_one(sql, name = nil)
197
+ # result = execute(sql, name)
198
+ # result.each(as: :hash) do |r|
199
+ # return r
200
+ # end
201
+ # end
202
+ #
203
+ # # Returns a single value from a record
204
+ # def select_value(sql, name = nil)
205
+ # result = execute(sql, name)
206
+ # if first = result.first
207
+ # first.first
208
+ # end
209
+ # end
210
+ #
211
+ # # Returns an array of the values of the first column in a select:
212
+ # # select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
213
+ # def select_values(sql, name = nil)
214
+ # execute(sql, name).map { |row| row.first }
215
+ # end
216
+
217
+ # Returns an array of arrays containing the field values.
218
+ # Order is the same as that returned by +columns+.
219
+ def select_rows(sql, name = nil, binds = [])
220
+ execute(sql, name).to_a
221
+ end
222
+
223
+ # Executes the SQL statement in the context of this connection.
224
+ def execute(sql, name = nil)
225
+ if @connection
226
+ # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
227
+ # made since we established the connection
228
+ @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
229
+ end
230
+
231
+ super
232
+ end
233
+
234
+ def exec_query(sql, name = 'SQL', binds = [])
235
+ result = execute(sql, name)
236
+ ActiveRecord::Result.new(result.fields, result.to_a)
237
+ end
238
+
239
+ alias exec_without_stmt exec_query
240
+
241
+ def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
242
+ super
243
+ id_value || @connection.last_id
244
+ end
245
+ alias :create :insert_sql
246
+
247
+ def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
248
+ execute to_sql(sql, binds), name
249
+ end
250
+
251
+ def exec_delete(sql, name, binds)
252
+ execute to_sql(sql, binds), name
253
+ @connection.affected_rows
254
+ end
255
+ alias :exec_update :exec_delete
256
+
257
+ def last_inserted_id(result)
258
+ @connection.last_id
259
+ end
260
+
261
+ private
262
+
263
+ def connect
264
+ @connection = Mysql2::Client.new(@config)
265
+ configure_connection
266
+ end
267
+
268
+ def configure_connection
269
+ @connection.query_options.merge!(:as => :array)
270
+ super
271
+ end
272
+
273
+ def full_version
274
+ @full_version ||= @connection.info[:version]
275
+ end
276
+
277
+ def set_field_encoding field_name
278
+ field_name
279
+ end
280
+ end
281
+ end
282
+ end