activerecord 4.2.0 → 5.0.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 (249) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1537 -789
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +7 -8
  5. data/examples/performance.rb +2 -3
  6. data/examples/simple.rb +0 -1
  7. data/lib/active_record/aggregations.rb +37 -23
  8. data/lib/active_record/association_relation.rb +16 -3
  9. data/lib/active_record/associations/alias_tracker.rb +19 -16
  10. data/lib/active_record/associations/association.rb +23 -9
  11. data/lib/active_record/associations/association_scope.rb +74 -102
  12. data/lib/active_record/associations/belongs_to_association.rb +26 -29
  13. data/lib/active_record/associations/builder/association.rb +28 -34
  14. data/lib/active_record/associations/builder/belongs_to.rb +43 -18
  15. data/lib/active_record/associations/builder/collection_association.rb +12 -20
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +22 -15
  17. data/lib/active_record/associations/builder/has_many.rb +4 -4
  18. data/lib/active_record/associations/builder/has_one.rb +11 -6
  19. data/lib/active_record/associations/builder/singular_association.rb +3 -10
  20. data/lib/active_record/associations/collection_association.rb +61 -33
  21. data/lib/active_record/associations/collection_proxy.rb +81 -35
  22. data/lib/active_record/associations/foreign_association.rb +11 -0
  23. data/lib/active_record/associations/has_many_association.rb +21 -57
  24. data/lib/active_record/associations/has_many_through_association.rb +15 -45
  25. data/lib/active_record/associations/has_one_association.rb +13 -5
  26. data/lib/active_record/associations/join_dependency/join_association.rb +20 -8
  27. data/lib/active_record/associations/join_dependency.rb +37 -21
  28. data/lib/active_record/associations/preloader/association.rb +51 -53
  29. data/lib/active_record/associations/preloader/collection_association.rb +0 -6
  30. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  31. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  32. data/lib/active_record/associations/preloader/through_association.rb +27 -14
  33. data/lib/active_record/associations/preloader.rb +18 -8
  34. data/lib/active_record/associations/singular_association.rb +8 -8
  35. data/lib/active_record/associations/through_association.rb +22 -9
  36. data/lib/active_record/associations.rb +321 -212
  37. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  38. data/lib/active_record/attribute.rb +79 -15
  39. data/lib/active_record/attribute_assignment.rb +20 -141
  40. data/lib/active_record/attribute_decorators.rb +6 -5
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +6 -1
  42. data/lib/active_record/attribute_methods/dirty.rb +51 -81
  43. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  44. data/lib/active_record/attribute_methods/query.rb +2 -2
  45. data/lib/active_record/attribute_methods/read.rb +31 -59
  46. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -14
  48. data/lib/active_record/attribute_methods/write.rb +14 -38
  49. data/lib/active_record/attribute_methods.rb +70 -45
  50. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  51. data/lib/active_record/attribute_set/builder.rb +37 -15
  52. data/lib/active_record/attribute_set.rb +34 -3
  53. data/lib/active_record/attributes.rb +199 -73
  54. data/lib/active_record/autosave_association.rb +73 -25
  55. data/lib/active_record/base.rb +35 -27
  56. data/lib/active_record/callbacks.rb +39 -43
  57. data/lib/active_record/coders/json.rb +1 -1
  58. data/lib/active_record/coders/yaml_column.rb +20 -8
  59. data/lib/active_record/collection_cache_key.rb +40 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +457 -181
  61. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  62. data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -59
  63. data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -3
  64. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -9
  65. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -4
  66. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
  67. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +246 -185
  68. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
  69. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +438 -136
  70. data/lib/active_record/connection_adapters/abstract/transaction.rb +53 -40
  71. data/lib/active_record/connection_adapters/abstract_adapter.rb +166 -66
  72. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +429 -335
  73. data/lib/active_record/connection_adapters/column.rb +28 -43
  74. data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
  75. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  76. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  77. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  78. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  79. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  81. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  82. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  83. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  84. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -177
  85. data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
  86. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +11 -73
  87. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -56
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -13
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  95. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  97. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
  98. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  99. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
  101. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +17 -5
  102. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  103. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  106. data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
  107. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  108. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  109. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +248 -154
  111. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  112. data/lib/active_record/connection_adapters/postgresql_adapter.rb +258 -170
  113. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  114. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  115. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  116. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  117. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +150 -209
  119. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  120. data/lib/active_record/connection_handling.rb +38 -15
  121. data/lib/active_record/core.rb +109 -114
  122. data/lib/active_record/counter_cache.rb +14 -25
  123. data/lib/active_record/dynamic_matchers.rb +1 -20
  124. data/lib/active_record/enum.rb +115 -79
  125. data/lib/active_record/errors.rb +88 -48
  126. data/lib/active_record/explain_registry.rb +1 -1
  127. data/lib/active_record/explain_subscriber.rb +2 -2
  128. data/lib/active_record/fixture_set/file.rb +26 -5
  129. data/lib/active_record/fixtures.rb +84 -46
  130. data/lib/active_record/gem_version.rb +2 -2
  131. data/lib/active_record/inheritance.rb +32 -40
  132. data/lib/active_record/integration.rb +4 -4
  133. data/lib/active_record/internal_metadata.rb +56 -0
  134. data/lib/active_record/legacy_yaml_adapter.rb +46 -0
  135. data/lib/active_record/locale/en.yml +3 -2
  136. data/lib/active_record/locking/optimistic.rb +27 -25
  137. data/lib/active_record/locking/pessimistic.rb +1 -1
  138. data/lib/active_record/log_subscriber.rb +43 -21
  139. data/lib/active_record/migration/command_recorder.rb +59 -18
  140. data/lib/active_record/migration/compatibility.rb +126 -0
  141. data/lib/active_record/migration.rb +372 -114
  142. data/lib/active_record/model_schema.rb +128 -38
  143. data/lib/active_record/nested_attributes.rb +71 -32
  144. data/lib/active_record/no_touching.rb +1 -1
  145. data/lib/active_record/null_relation.rb +16 -8
  146. data/lib/active_record/persistence.rb +124 -80
  147. data/lib/active_record/query_cache.rb +15 -18
  148. data/lib/active_record/querying.rb +10 -9
  149. data/lib/active_record/railtie.rb +28 -19
  150. data/lib/active_record/railties/controller_runtime.rb +1 -1
  151. data/lib/active_record/railties/databases.rake +67 -51
  152. data/lib/active_record/readonly_attributes.rb +1 -1
  153. data/lib/active_record/reflection.rb +318 -139
  154. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  155. data/lib/active_record/relation/batches.rb +139 -34
  156. data/lib/active_record/relation/calculations.rb +80 -102
  157. data/lib/active_record/relation/delegation.rb +7 -20
  158. data/lib/active_record/relation/finder_methods.rb +167 -97
  159. data/lib/active_record/relation/from_clause.rb +32 -0
  160. data/lib/active_record/relation/merger.rb +38 -41
  161. data/lib/active_record/relation/predicate_builder/array_handler.rb +12 -16
  162. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  163. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  164. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  165. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  166. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  167. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  168. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  169. data/lib/active_record/relation/predicate_builder.rb +124 -82
  170. data/lib/active_record/relation/query_attribute.rb +19 -0
  171. data/lib/active_record/relation/query_methods.rb +323 -257
  172. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  173. data/lib/active_record/relation/spawn_methods.rb +11 -10
  174. data/lib/active_record/relation/where_clause.rb +174 -0
  175. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  176. data/lib/active_record/relation.rb +176 -115
  177. data/lib/active_record/result.rb +4 -3
  178. data/lib/active_record/runtime_registry.rb +1 -1
  179. data/lib/active_record/sanitization.rb +95 -66
  180. data/lib/active_record/schema.rb +26 -22
  181. data/lib/active_record/schema_dumper.rb +62 -38
  182. data/lib/active_record/schema_migration.rb +11 -17
  183. data/lib/active_record/scoping/default.rb +24 -9
  184. data/lib/active_record/scoping/named.rb +49 -28
  185. data/lib/active_record/scoping.rb +32 -15
  186. data/lib/active_record/secure_token.rb +38 -0
  187. data/lib/active_record/serialization.rb +2 -4
  188. data/lib/active_record/statement_cache.rb +16 -14
  189. data/lib/active_record/store.rb +8 -3
  190. data/lib/active_record/suppressor.rb +58 -0
  191. data/lib/active_record/table_metadata.rb +68 -0
  192. data/lib/active_record/tasks/database_tasks.rb +59 -42
  193. data/lib/active_record/tasks/mysql_database_tasks.rb +32 -26
  194. data/lib/active_record/tasks/postgresql_database_tasks.rb +29 -9
  195. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  196. data/lib/active_record/timestamp.rb +20 -9
  197. data/lib/active_record/touch_later.rb +58 -0
  198. data/lib/active_record/transactions.rb +159 -67
  199. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  200. data/lib/active_record/type/date.rb +2 -41
  201. data/lib/active_record/type/date_time.rb +2 -38
  202. data/lib/active_record/type/hash_lookup_type_map.rb +8 -2
  203. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  204. data/lib/active_record/type/internal/timezone.rb +15 -0
  205. data/lib/active_record/type/serialized.rb +21 -14
  206. data/lib/active_record/type/time.rb +10 -16
  207. data/lib/active_record/type/type_map.rb +4 -4
  208. data/lib/active_record/type.rb +66 -17
  209. data/lib/active_record/type_caster/connection.rb +29 -0
  210. data/lib/active_record/type_caster/map.rb +19 -0
  211. data/lib/active_record/type_caster.rb +7 -0
  212. data/lib/active_record/validations/absence.rb +23 -0
  213. data/lib/active_record/validations/associated.rb +10 -3
  214. data/lib/active_record/validations/length.rb +24 -0
  215. data/lib/active_record/validations/presence.rb +11 -12
  216. data/lib/active_record/validations/uniqueness.rb +29 -18
  217. data/lib/active_record/validations.rb +33 -32
  218. data/lib/active_record.rb +9 -2
  219. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  220. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -6
  221. data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -7
  222. data/lib/rails/generators/active_record/migration.rb +7 -0
  223. data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
  224. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  225. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  226. metadata +60 -34
  227. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  228. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  229. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  230. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  231. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  232. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  233. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  234. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  235. data/lib/active_record/type/big_integer.rb +0 -13
  236. data/lib/active_record/type/binary.rb +0 -50
  237. data/lib/active_record/type/boolean.rb +0 -30
  238. data/lib/active_record/type/decimal.rb +0 -40
  239. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  240. data/lib/active_record/type/decorator.rb +0 -14
  241. data/lib/active_record/type/float.rb +0 -19
  242. data/lib/active_record/type/integer.rb +0 -55
  243. data/lib/active_record/type/mutable.rb +0 -16
  244. data/lib/active_record/type/numeric.rb +0 -36
  245. data/lib/active_record/type/string.rb +0 -36
  246. data/lib/active_record/type/text.rb +0 -11
  247. data/lib/active_record/type/time_value.rb +0 -38
  248. data/lib/active_record/type/unsigned_integer.rb +0 -15
  249. data/lib/active_record/type/value.rb +0 -101
@@ -0,0 +1,54 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module MySQL
4
+ module ColumnDumper
5
+ def column_spec_for_primary_key(column)
6
+ if column.bigint?
7
+ spec = { id: :bigint.inspect }
8
+ spec[:default] = schema_default(column) || 'nil' unless column.auto_increment?
9
+ else
10
+ spec = super
11
+ end
12
+ spec[:unsigned] = 'true' if column.unsigned?
13
+ spec
14
+ end
15
+
16
+ def prepare_column_options(column)
17
+ spec = super
18
+ spec[:unsigned] = 'true' if column.unsigned?
19
+ spec
20
+ end
21
+
22
+ def migration_keys
23
+ super + [:unsigned]
24
+ end
25
+
26
+ private
27
+
28
+ def default_primary_key?(column)
29
+ super && column.auto_increment?
30
+ end
31
+
32
+ def schema_type(column)
33
+ if column.sql_type == 'tinyblob'
34
+ :blob
35
+ else
36
+ super
37
+ end
38
+ end
39
+
40
+ def schema_precision(column)
41
+ super unless /time/ === column.sql_type && column.precision == 0
42
+ end
43
+
44
+ def schema_collation(column)
45
+ if column.collation && table_name = column.table_name
46
+ @table_collation_cache ||= {}
47
+ @table_collation_cache[table_name] ||= select_one("SHOW TABLE STATUS LIKE '#{table_name}'")["Collation"]
48
+ column.collation.inspect if column.collation != @table_collation_cache[table_name]
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,32 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module MySQL
4
+ class TypeMetadata < DelegateClass(SqlTypeMetadata) # :nodoc:
5
+ attr_reader :extra, :strict
6
+
7
+ def initialize(type_metadata, extra: "", strict: false)
8
+ super(type_metadata)
9
+ @type_metadata = type_metadata
10
+ @extra = extra
11
+ @strict = strict
12
+ end
13
+
14
+ def ==(other)
15
+ other.is_a?(MySQL::TypeMetadata) &&
16
+ attributes_for_hash == other.attributes_for_hash
17
+ end
18
+ alias eql? ==
19
+
20
+ def hash
21
+ attributes_for_hash.hash
22
+ end
23
+
24
+ protected
25
+
26
+ def attributes_for_hash
27
+ [self.class, @type_metadata, extra, strict]
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -1,7 +1,9 @@
1
1
  require 'active_record/connection_adapters/abstract_mysql_adapter'
2
+ require 'active_record/connection_adapters/mysql/database_statements'
2
3
 
3
- gem 'mysql2', '~> 0.3.13'
4
+ gem 'mysql2', '>= 0.3.18', '< 0.5'
4
5
  require 'mysql2'
6
+ raise 'mysql2 0.4.3 is not supported. Please upgrade to 0.4.4+' if Mysql2::VERSION == '0.4.3'
5
7
 
6
8
  module ActiveRecord
7
9
  module ConnectionHandling # :nodoc:
@@ -10,17 +12,21 @@ module ActiveRecord
10
12
  config = config.symbolize_keys
11
13
 
12
14
  config[:username] = 'root' if config[:username].nil?
15
+ config[:flags] ||= 0
13
16
 
14
17
  if Mysql2::Client.const_defined? :FOUND_ROWS
15
- config[:flags] = Mysql2::Client::FOUND_ROWS
18
+ if config[:flags].kind_of? Array
19
+ config[:flags].push "FOUND_ROWS".freeze
20
+ else
21
+ config[:flags] |= Mysql2::Client::FOUND_ROWS
22
+ end
16
23
  end
17
24
 
18
25
  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)
26
+ ConnectionAdapters::Mysql2Adapter.new(client, logger, nil, config)
21
27
  rescue Mysql2::Error => error
22
28
  if error.message.include?("Unknown database")
23
- raise ActiveRecord::NoDatabaseError.new(error.message, error)
29
+ raise ActiveRecord::NoDatabaseError
24
30
  else
25
31
  raise
26
32
  end
@@ -31,22 +37,27 @@ module ActiveRecord
31
37
  class Mysql2Adapter < AbstractMysqlAdapter
32
38
  ADAPTER_NAME = 'Mysql2'.freeze
33
39
 
40
+ include MySQL::DatabaseStatements
41
+
34
42
  def initialize(connection, logger, connection_options, config)
35
43
  super
36
- @prepared_statements = false
44
+ @prepared_statements = false unless config.key?(:prepared_statements)
37
45
  configure_connection
38
46
  end
39
47
 
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
48
+ def supports_json?
49
+ !mariadb? && version >= '5.7.8'
50
+ end
51
+
52
+ def supports_comments?
53
+ true
47
54
  end
48
55
 
49
- def supports_explain?
56
+ def supports_comments_in_create?
57
+ true
58
+ end
59
+
60
+ def supports_savepoints?
50
61
  true
51
62
  end
52
63
 
@@ -74,14 +85,6 @@ module ActiveRecord
74
85
  @connection.escape(string)
75
86
  end
76
87
 
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
88
  #--
86
89
  # CONNECTION MANAGEMENT ====================================
87
90
  #++
@@ -108,156 +111,6 @@ module ActiveRecord
108
111
  end
109
112
  end
110
113
 
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
114
  private
262
115
 
263
116
  def connect
@@ -271,11 +124,7 @@ module ActiveRecord
271
124
  end
272
125
 
273
126
  def full_version
274
- @full_version ||= @connection.info[:version]
275
- end
276
-
277
- def set_field_encoding field_name
278
- field_name
127
+ @full_version ||= @connection.server_info[:version]
279
128
  end
280
129
  end
281
130
  end
@@ -2,18 +2,13 @@ module ActiveRecord
2
2
  module ConnectionAdapters
3
3
  # PostgreSQL-specific extensions to column definitions in a table.
4
4
  class PostgreSQLColumn < Column #:nodoc:
5
- attr_accessor :array
5
+ delegate :array, :oid, :fmod, to: :sql_type_metadata
6
+ alias :array? :array
6
7
 
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
8
+ def serial?
9
+ return unless default_function
15
10
 
16
- @default_function = default_function
11
+ %r{\Anextval\('"?#{table_name}_#{name}_seq"?'::regclass\)\z} === default_function
17
12
  end
18
13
  end
19
14
  end
@@ -4,44 +4,7 @@ module ActiveRecord
4
4
  module DatabaseStatements
5
5
  def explain(arel, binds = [])
6
6
  sql = "EXPLAIN #{to_sql(arel, binds)}"
7
- ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', binds))
8
- end
9
-
10
- class ExplainPrettyPrinter # :nodoc:
11
- # Pretty prints the result of a EXPLAIN in a way that resembles the output of the
12
- # PostgreSQL shell:
13
- #
14
- # QUERY PLAN
15
- # ------------------------------------------------------------------------------
16
- # Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0)
17
- # Join Filter: (posts.user_id = users.id)
18
- # -> Index Scan using users_pkey on users (cost=0.00..8.27 rows=1 width=4)
19
- # Index Cond: (id = 1)
20
- # -> Seq Scan on posts (cost=0.00..28.88 rows=8 width=4)
21
- # Filter: (posts.user_id = 1)
22
- # (6 rows)
23
- #
24
- def pp(result)
25
- header = result.columns.first
26
- lines = result.rows.map(&:first)
27
-
28
- # We add 2 because there's one char of padding at both sides, note
29
- # the extra hyphens in the example above.
30
- width = [header, *lines].map(&:length).max + 2
31
-
32
- pp = []
33
-
34
- pp << header.center(width).rstrip
35
- pp << '-' * width
36
-
37
- pp += lines.map {|line| " #{line}"}
38
-
39
- nrows = result.rows.length
40
- rows_label = nrows == 1 ? 'row' : 'rows'
41
- pp << "(#{nrows} #{rows_label})"
42
-
43
- pp.join("\n") + "\n"
44
- end
7
+ PostgreSQL::ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', binds))
45
8
  end
46
9
 
47
10
  def select_value(arel, name = nil, binds = [])
@@ -52,8 +15,8 @@ module ActiveRecord
52
15
  end
53
16
  end
54
17
 
55
- def select_values(arel, name = nil)
56
- arel, binds = binds_from_relation arel, []
18
+ def select_values(arel, name = nil, binds = [])
19
+ arel, binds = binds_from_relation arel, binds
57
20
  sql = to_sql(arel, binds)
58
21
  execute_and_clear(sql, name, binds) do |result|
59
22
  if result.nfields > 0
@@ -72,28 +35,6 @@ module ActiveRecord
72
35
  end
73
36
  end
74
37
 
75
- # Executes an INSERT query and returns the new record's ID
76
- def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
77
- unless pk
78
- # Extract the table from the insert sql. Yuck.
79
- table_ref = extract_table_ref_from_insert_sql(sql)
80
- pk = primary_key(table_ref) if table_ref
81
- end
82
-
83
- if pk && use_insert_returning?
84
- select_value("#{sql} RETURNING #{quote_column_name(pk)}")
85
- elsif pk
86
- super
87
- last_insert_id_value(sequence_name || default_sequence_name(table_ref, pk))
88
- else
89
- super
90
- end
91
- end
92
-
93
- def create
94
- super.insert
95
- end
96
-
97
38
  # The internal PostgreSQL identifier of the money data type.
98
39
  MONEY_COLUMN_TYPE_OID = 790 #:nodoc:
99
40
  # The internal PostgreSQL identifier of the BYTEA data type.
@@ -150,14 +91,16 @@ module ActiveRecord
150
91
 
151
92
  # Executes an SQL statement, returning a PGresult object on success
152
93
  # or raising a PGError exception otherwise.
94
+ # Note: the PGresult object is manually memory managed; if you don't
95
+ # need it specifically, you many want consider the exec_query wrapper.
153
96
  def execute(sql, name = nil)
154
97
  log(sql, name) do
155
98
  @connection.async_exec(sql)
156
99
  end
157
100
  end
158
101
 
159
- def exec_query(sql, name = 'SQL', binds = [])
160
- execute_and_clear(sql, name, binds) do |result|
102
+ def exec_query(sql, name = 'SQL', binds = [], prepare: false)
103
+ execute_and_clear(sql, name, binds, prepare: prepare) do |result|
161
104
  types = {}
162
105
  fields = result.fields
163
106
  fields.each_with_index do |fname, i|
@@ -174,8 +117,8 @@ module ActiveRecord
174
117
  end
175
118
  alias :exec_update :exec_delete
176
119
 
177
- def sql_for_insert(sql, pk, id_value, sequence_name, binds)
178
- unless pk
120
+ def sql_for_insert(sql, pk, id_value, sequence_name, binds) # :nodoc:
121
+ if pk.nil?
179
122
  # Extract the table from the insert sql. Yuck.
180
123
  table_ref = extract_table_ref_from_insert_sql(sql)
181
124
  pk = primary_key(table_ref) if table_ref
@@ -185,7 +128,7 @@ module ActiveRecord
185
128
  sql = "#{sql} RETURNING #{quote_column_name(pk)}"
186
129
  end
187
130
 
188
- [sql, binds]
131
+ super
189
132
  end
190
133
 
191
134
  def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
@@ -202,11 +145,6 @@ module ActiveRecord
202
145
  end
203
146
  end
204
147
 
205
- # Executes an UPDATE query and returns the number of affected tuples.
206
- def update_sql(sql, name = nil)
207
- super.cmd_tuples
208
- end
209
-
210
148
  # Begins a transaction.
211
149
  def begin_db_transaction
212
150
  execute "BEGIN"
@@ -223,7 +161,7 @@ module ActiveRecord
223
161
  end
224
162
 
225
163
  # Aborts a transaction.
226
- def rollback_db_transaction
164
+ def exec_rollback_db_transaction
227
165
  execute "ROLLBACK"
228
166
  end
229
167
  end
@@ -0,0 +1,42 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ class ExplainPrettyPrinter # :nodoc:
5
+ # Pretty prints the result of an EXPLAIN in a way that resembles the output of the
6
+ # PostgreSQL shell:
7
+ #
8
+ # QUERY PLAN
9
+ # ------------------------------------------------------------------------------
10
+ # Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0)
11
+ # Join Filter: (posts.user_id = users.id)
12
+ # -> Index Scan using users_pkey on users (cost=0.00..8.27 rows=1 width=4)
13
+ # Index Cond: (id = 1)
14
+ # -> Seq Scan on posts (cost=0.00..28.88 rows=8 width=4)
15
+ # Filter: (posts.user_id = 1)
16
+ # (6 rows)
17
+ #
18
+ def pp(result)
19
+ header = result.columns.first
20
+ lines = result.rows.map(&:first)
21
+
22
+ # We add 2 because there's one char of padding at both sides, note
23
+ # the extra hyphens in the example above.
24
+ width = [header, *lines].map(&:length).max + 2
25
+
26
+ pp = []
27
+
28
+ pp << header.center(width).rstrip
29
+ pp << '-' * width
30
+
31
+ pp += lines.map {|line| " #{line}"}
32
+
33
+ nrows = result.rows.length
34
+ rows_label = nrows == 1 ? 'row' : 'rows'
35
+ pp << "(#{nrows} #{rows_label})"
36
+
37
+ pp.join("\n") + "\n"
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -3,51 +3,57 @@ module ActiveRecord
3
3
  module PostgreSQL
4
4
  module OID # :nodoc:
5
5
  class Array < Type::Value # :nodoc:
6
- include Type::Mutable
7
-
8
- # Loads pg_array_parser if available. String parsing can be
9
- # performed quicker by a native extension, which will not create
10
- # a large amount of Ruby objects that will need to be garbage
11
- # collected. pg_array_parser has a C and Java extension
12
- begin
13
- require 'pg_array_parser'
14
- include PgArrayParser
15
- rescue LoadError
16
- require 'active_record/connection_adapters/postgresql/array_parser'
17
- include PostgreSQL::ArrayParser
18
- end
6
+ include Type::Helpers::Mutable
19
7
 
20
8
  attr_reader :subtype, :delimiter
21
- delegate :type, to: :subtype
9
+ delegate :type, :user_input_in_time_zone, :limit, to: :subtype
22
10
 
23
11
  def initialize(subtype, delimiter = ',')
24
12
  @subtype = subtype
25
13
  @delimiter = delimiter
14
+
15
+ @pg_encoder = PG::TextEncoder::Array.new name: "#{type}[]", delimiter: delimiter
16
+ @pg_decoder = PG::TextDecoder::Array.new name: "#{type}[]", delimiter: delimiter
26
17
  end
27
18
 
28
- def type_cast_from_database(value)
19
+ def deserialize(value)
29
20
  if value.is_a?(::String)
30
- type_cast_array(parse_pg_array(value), :type_cast_from_database)
21
+ type_cast_array(@pg_decoder.decode(value), :deserialize)
31
22
  else
32
23
  super
33
24
  end
34
25
  end
35
26
 
36
- def type_cast_from_user(value)
27
+ def cast(value)
37
28
  if value.is_a?(::String)
38
- value = parse_pg_array(value)
29
+ value = @pg_decoder.decode(value)
39
30
  end
40
- type_cast_array(value, :type_cast_from_user)
31
+ type_cast_array(value, :cast)
41
32
  end
42
33
 
43
- def type_cast_for_database(value)
34
+ def serialize(value)
44
35
  if value.is_a?(::Array)
45
- cast_value_for_database(value)
36
+ @pg_encoder.encode(type_cast_array(value, :serialize))
46
37
  else
47
38
  super
48
39
  end
49
40
  end
50
41
 
42
+ def ==(other)
43
+ other.is_a?(Array) &&
44
+ subtype == other.subtype &&
45
+ delimiter == other.delimiter
46
+ end
47
+
48
+ def type_cast_for_schema(value)
49
+ return super unless value.is_a?(::Array)
50
+ "[" + value.map { |v| subtype.type_cast_for_schema(v) }.join(", ") + "]"
51
+ end
52
+
53
+ def map(value, &block)
54
+ value.map(&block)
55
+ end
56
+
51
57
  private
52
58
 
53
59
  def type_cast_array(value, method)
@@ -57,41 +63,6 @@ module ActiveRecord
57
63
  @subtype.public_send(method, value)
58
64
  end
59
65
  end
60
-
61
- def cast_value_for_database(value)
62
- if value.is_a?(::Array)
63
- casted_values = value.map { |item| cast_value_for_database(item) }
64
- "{#{casted_values.join(delimiter)}}"
65
- else
66
- quote_and_escape(subtype.type_cast_for_database(value))
67
- end
68
- end
69
-
70
- ARRAY_ESCAPE = "\\" * 2 * 2 # escape the backslash twice for PG arrays
71
-
72
- def quote_and_escape(value)
73
- case value
74
- when ::String
75
- if string_requires_quoting?(value)
76
- value = value.gsub(/\\/, ARRAY_ESCAPE)
77
- value.gsub!(/"/,"\\\"")
78
- %("#{value}")
79
- else
80
- value
81
- end
82
- when nil then "NULL"
83
- else value
84
- end
85
- end
86
-
87
- # See http://www.postgresql.org/docs/9.2/static/arrays.html#ARRAYS-IO
88
- # for a list of all cases in which strings will be quoted.
89
- def string_requires_quoting?(string)
90
- string.empty? ||
91
- string == "NULL" ||
92
- string =~ /[\{\}"\\\s]/ ||
93
- string.include?(delimiter)
94
- end
95
66
  end
96
67
  end
97
68
  end
@@ -7,7 +7,7 @@ module ActiveRecord
7
7
  :bit
8
8
  end
9
9
 
10
- def type_cast(value)
10
+ def cast(value)
11
11
  if ::String === value
12
12
  case value
13
13
  when /^0x/i
@@ -20,7 +20,7 @@ module ActiveRecord
20
20
  end
21
21
  end
22
22
 
23
- def type_cast_for_database(value)
23
+ def serialize(value)
24
24
  Data.new(super) if value
25
25
  end
26
26