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
@@ -1,13 +1,14 @@
1
1
  require 'active_record/connection_adapters/abstract_adapter'
2
2
  require 'active_record/connection_adapters/statement_pool'
3
- require 'arel/visitors/bind_visitor'
3
+ require 'active_record/connection_adapters/sqlite3/explain_pretty_printer'
4
+ require 'active_record/connection_adapters/sqlite3/quoting'
5
+ require 'active_record/connection_adapters/sqlite3/schema_creation'
4
6
 
5
7
  gem 'sqlite3', '~> 1.3.6'
6
8
  require 'sqlite3'
7
9
 
8
10
  module ActiveRecord
9
11
  module ConnectionHandling # :nodoc:
10
- # sqlite3 adapter reuses sqlite_connection.
11
12
  def sqlite3_connection(config)
12
13
  # Require database.
13
14
  unless config[:database]
@@ -33,7 +34,7 @@ module ActiveRecord
33
34
  ConnectionAdapters::SQLite3Adapter.new(db, logger, nil, config)
34
35
  rescue Errno::ENOENT => error
35
36
  if error.message.include?("No such file or directory")
36
- raise ActiveRecord::NoDatabaseError.new(error.message, error)
37
+ raise ActiveRecord::NoDatabaseError
37
38
  else
38
39
  raise
39
40
  end
@@ -41,25 +42,6 @@ module ActiveRecord
41
42
  end
42
43
 
43
44
  module ConnectionAdapters #:nodoc:
44
- class SQLite3Binary < Type::Binary # :nodoc:
45
- def cast_value(value)
46
- if value.encoding != Encoding::ASCII_8BIT
47
- value = value.force_encoding(Encoding::ASCII_8BIT)
48
- end
49
- value
50
- end
51
- end
52
-
53
- class SQLite3String < Type::String # :nodoc:
54
- def type_cast_for_database(value)
55
- if value.is_a?(::String) && value.encoding == Encoding::ASCII_8BIT
56
- value.encode(Encoding::UTF_8)
57
- else
58
- super
59
- end
60
- end
61
- end
62
-
63
45
  # The SQLite3 adapter works SQLite 3.6.16 or newer
64
46
  # with the sqlite3-ruby drivers (available as gem from https://rubygems.org/gems/sqlite3).
65
47
  #
@@ -68,7 +50,8 @@ module ActiveRecord
68
50
  # * <tt>:database</tt> - Path to the database file.
69
51
  class SQLite3Adapter < AbstractAdapter
70
52
  ADAPTER_NAME = 'SQLite'.freeze
71
- include Savepoints
53
+
54
+ include SQLite3::Quoting
72
55
 
73
56
  NATIVE_DATABASE_TYPES = {
74
57
  primary_key: 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL',
@@ -84,68 +67,27 @@ module ActiveRecord
84
67
  boolean: { name: "boolean" }
85
68
  }
86
69
 
87
- class Version
88
- include Comparable
89
-
90
- def initialize(version_string)
91
- @version = version_string.split('.').map { |v| v.to_i }
92
- end
93
-
94
- def <=>(version_string)
95
- @version <=> version_string.split('.').map { |v| v.to_i }
96
- end
97
- end
98
-
99
70
  class StatementPool < ConnectionAdapters::StatementPool
100
- def initialize(connection, max)
101
- super
102
- @cache = Hash.new { |h,pid| h[pid] = {} }
103
- end
104
-
105
- def each(&block); cache.each(&block); end
106
- def key?(key); cache.key?(key); end
107
- def [](key); cache[key]; end
108
- def length; cache.length; end
109
-
110
- def []=(sql, key)
111
- while @max <= cache.size
112
- dealloc(cache.shift.last[:stmt])
113
- end
114
- cache[sql] = key
115
- end
116
-
117
- def clear
118
- cache.each_value do |hash|
119
- dealloc hash[:stmt]
120
- end
121
- cache.clear
122
- end
123
-
124
71
  private
125
- def cache
126
- @cache[$$]
127
- end
128
72
 
129
73
  def dealloc(stmt)
130
- stmt.close unless stmt.closed?
74
+ stmt[:stmt].close unless stmt[:stmt].closed?
131
75
  end
132
76
  end
133
77
 
134
- def initialize(connection, logger, connection_options, config)
135
- super(connection, logger)
78
+ def schema_creation # :nodoc:
79
+ SQLite3::SchemaCreation.new self
80
+ end
136
81
 
137
- @active = nil
138
- @statements = StatementPool.new(@connection,
139
- self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 }))
140
- @config = config
82
+ def arel_visitor # :nodoc:
83
+ Arel::Visitors::SQLite.new(self)
84
+ end
141
85
 
142
- @visitor = Arel::Visitors::SQLite.new self
86
+ def initialize(connection, logger, connection_options, config)
87
+ super(connection, logger, config)
143
88
 
144
- if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
145
- @prepared_statements = true
146
- else
147
- @prepared_statements = false
148
- end
89
+ @active = nil
90
+ @statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
149
91
  end
150
92
 
151
93
  def supports_ddl_transactions?
@@ -183,6 +125,14 @@ module ActiveRecord
183
125
  true
184
126
  end
185
127
 
128
+ def supports_datetime_with_precision?
129
+ true
130
+ end
131
+
132
+ def supports_multi_insert?
133
+ sqlite_version >= '3.7.11'
134
+ end
135
+
186
136
  def active?
187
137
  @active != false
188
138
  end
@@ -204,6 +154,10 @@ module ActiveRecord
204
154
  true
205
155
  end
206
156
 
157
+ def valid_type?(type)
158
+ true
159
+ end
160
+
207
161
  # Returns 62. SQLite supports index names up to 64
208
162
  # characters. The rest is used by rails internally to perform
209
163
  # temporary rename operations
@@ -224,82 +178,27 @@ module ActiveRecord
224
178
  true
225
179
  end
226
180
 
227
- # QUOTING ==================================================
228
-
229
- def _quote(value) # :nodoc:
230
- case value
231
- when Type::Binary::Data
232
- "x'#{value.hex}'"
233
- else
234
- super
235
- end
236
- end
237
-
238
- def _type_cast(value) # :nodoc:
239
- case value
240
- when BigDecimal
241
- value.to_f
242
- else
243
- super
244
- end
245
- end
246
-
247
- def quote_string(s) #:nodoc:
248
- @connection.class.quote(s)
249
- end
250
-
251
- def quote_table_name_for_assignment(table, attr)
252
- quote_column_name(attr)
253
- end
254
-
255
- def quote_column_name(name) #:nodoc:
256
- %Q("#{name.to_s.gsub('"', '""')}")
257
- end
258
-
259
- # Quote date/time values for use in SQL input. Includes microseconds
260
- # if the value is a Time responding to usec.
261
- def quoted_date(value) #:nodoc:
262
- if value.respond_to?(:usec)
263
- "#{super}.#{sprintf("%06d", value.usec)}"
264
- else
265
- super
266
- end
267
- end
268
-
269
181
  #--
270
182
  # DATABASE STATEMENTS ======================================
271
183
  #++
272
184
 
273
185
  def explain(arel, binds = [])
274
186
  sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
275
- ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', []))
276
- end
277
-
278
- class ExplainPrettyPrinter
279
- # Pretty prints the result of a EXPLAIN QUERY PLAN in a way that resembles
280
- # the output of the SQLite shell:
281
- #
282
- # 0|0|0|SEARCH TABLE users USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
283
- # 0|1|1|SCAN TABLE posts (~100000 rows)
284
- #
285
- def pp(result) # :nodoc:
286
- result.rows.map do |row|
287
- row.join('|')
288
- end.join("\n") + "\n"
289
- end
187
+ SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', []))
290
188
  end
291
189
 
292
- def exec_query(sql, name = nil, binds = [])
293
- type_casted_binds = binds.map { |col, val|
294
- [col, type_cast(val, col)]
295
- }
190
+ def exec_query(sql, name = nil, binds = [], prepare: false)
191
+ type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) }
296
192
 
297
- log(sql, name, type_casted_binds) do
193
+ log(sql, name, binds) do
298
194
  # Don't cache statements if they are not prepared
299
- if without_prepared_statement?(binds)
195
+ unless prepare
300
196
  stmt = @connection.prepare(sql)
301
197
  begin
302
198
  cols = stmt.columns
199
+ unless without_prepared_statement?(binds)
200
+ stmt.bind_params(type_casted_binds)
201
+ end
303
202
  records = stmt.to_a
304
203
  ensure
305
204
  stmt.close
@@ -312,7 +211,7 @@ module ActiveRecord
312
211
  stmt = cache[:stmt]
313
212
  cols = cache[:cols] ||= stmt.columns
314
213
  stmt.reset!
315
- stmt.bind_params type_casted_binds.map { |_, val| val }
214
+ stmt.bind_params(type_casted_binds)
316
215
  end
317
216
 
318
217
  ActiveRecord::Result.new(cols, stmt.to_a)
@@ -333,26 +232,6 @@ module ActiveRecord
333
232
  log(sql, name) { @connection.execute(sql) }
334
233
  end
335
234
 
336
- def update_sql(sql, name = nil) #:nodoc:
337
- super
338
- @connection.changes
339
- end
340
-
341
- def delete_sql(sql, name = nil) #:nodoc:
342
- sql += " WHERE 1=1" unless sql =~ /WHERE/i
343
- super sql, name
344
- end
345
-
346
- def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
347
- super
348
- id_value || @connection.last_insert_row_id
349
- end
350
- alias :create :insert_sql
351
-
352
- def select_rows(sql, name = nil, binds = [])
353
- exec_query(sql, name, binds).rows
354
- end
355
-
356
235
  def begin_db_transaction #:nodoc:
357
236
  log('begin transaction',nil) { @connection.transaction }
358
237
  end
@@ -361,31 +240,67 @@ module ActiveRecord
361
240
  log('commit transaction',nil) { @connection.commit }
362
241
  end
363
242
 
364
- def rollback_db_transaction #:nodoc:
243
+ def exec_rollback_db_transaction #:nodoc:
365
244
  log('rollback transaction',nil) { @connection.rollback }
366
245
  end
367
246
 
368
247
  # SCHEMA STATEMENTS ========================================
369
248
 
370
- def tables(name = nil, table_name = nil) #:nodoc:
371
- sql = <<-SQL
372
- SELECT name
373
- FROM sqlite_master
374
- WHERE (type = 'table' OR type = 'view') AND NOT name = 'sqlite_sequence'
375
- SQL
376
- sql << " AND name = #{quote_table_name(table_name)}" if table_name
249
+ def tables(name = nil) # :nodoc:
250
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
251
+ #tables currently returns both tables and views.
252
+ This behavior is deprecated and will be changed with Rails 5.1 to only return tables.
253
+ Use #data_sources instead.
254
+ MSG
377
255
 
378
- exec_query(sql, 'SCHEMA').map do |row|
379
- row['name']
256
+ if name
257
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
258
+ Passing arguments to #tables is deprecated without replacement.
259
+ MSG
380
260
  end
261
+
262
+ data_sources
263
+ end
264
+
265
+ def data_sources
266
+ select_values("SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name <> 'sqlite_sequence'", 'SCHEMA')
381
267
  end
382
268
 
383
269
  def table_exists?(table_name)
384
- table_name && tables(nil, table_name).any?
270
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
271
+ #table_exists? currently checks both tables and views.
272
+ This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
273
+ Use #data_source_exists? instead.
274
+ MSG
275
+
276
+ data_source_exists?(table_name)
277
+ end
278
+
279
+ def data_source_exists?(table_name)
280
+ return false unless table_name.present?
281
+
282
+ sql = "SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name <> 'sqlite_sequence'"
283
+ sql << " AND name = #{quote(table_name)}"
284
+
285
+ select_values(sql, 'SCHEMA').any?
286
+ end
287
+
288
+ def views # :nodoc:
289
+ select_values("SELECT name FROM sqlite_master WHERE type = 'view' AND name <> 'sqlite_sequence'", 'SCHEMA')
290
+ end
291
+
292
+ def view_exists?(view_name) # :nodoc:
293
+ return false unless view_name.present?
294
+
295
+ sql = "SELECT name FROM sqlite_master WHERE type = 'view' AND name <> 'sqlite_sequence'"
296
+ sql << " AND name = #{quote(view_name)}"
297
+
298
+ select_values(sql, 'SCHEMA').any?
385
299
  end
386
300
 
387
301
  # Returns an array of +Column+ objects for the table specified by +table_name+.
388
- def columns(table_name) #:nodoc:
302
+ def columns(table_name) # :nodoc:
303
+ table_name = table_name.to_s
389
304
  table_structure(table_name).map do |field|
390
305
  case field["dflt_value"]
391
306
  when /^null$/i
@@ -396,9 +311,10 @@ module ActiveRecord
396
311
  field["dflt_value"] = $1.gsub('""', '"')
397
312
  end
398
313
 
314
+ collation = field['collation']
399
315
  sql_type = field['type']
400
- cast_type = lookup_cast_type(sql_type)
401
- new_column(field['name'], field['dflt_value'], cast_type, sql_type, field['notnull'].to_i == 0)
316
+ type_metadata = fetch_type_metadata(sql_type)
317
+ new_column(field['name'], field['dflt_value'], type_metadata, field['notnull'].to_i == 0, table_name, nil, collation)
402
318
  end
403
319
  end
404
320
 
@@ -427,14 +343,13 @@ module ActiveRecord
427
343
  end
428
344
  end
429
345
 
430
- def primary_key(table_name) #:nodoc:
431
- column = table_structure(table_name).find { |field|
432
- field['pk'] == 1
433
- }
434
- column && column['name']
346
+ def primary_keys(table_name) # :nodoc:
347
+ pks = table_structure(table_name).select { |f| f['pk'] > 0 }
348
+ pks.sort_by { |f| f['pk'] }.map { |f| f['name'] }
435
349
  end
436
350
 
437
- def remove_index!(table_name, index_name) #:nodoc:
351
+ def remove_index(table_name, options = {}) #:nodoc:
352
+ index_name = index_name_for_remove(table_name, options)
438
353
  exec_query "DROP INDEX #{quote_column_name(index_name)}"
439
354
  end
440
355
 
@@ -469,13 +384,15 @@ module ActiveRecord
469
384
  end
470
385
  end
471
386
 
472
- def change_column_default(table_name, column_name, default) #:nodoc:
387
+ def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
388
+ default = extract_new_default_value(default_or_changes)
389
+
473
390
  alter_table(table_name) do |definition|
474
391
  definition[column_name].default = default
475
392
  end
476
393
  end
477
394
 
478
- def change_column_null(table_name, column_name, null, default = nil)
395
+ def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
479
396
  unless null || default.nil?
480
397
  exec_query("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
481
398
  end
@@ -494,6 +411,7 @@ module ActiveRecord
494
411
  self.null = options[:null] if options.include?(:null)
495
412
  self.precision = options[:precision] if options.include?(:precision)
496
413
  self.scale = options[:scale] if options.include?(:scale)
414
+ self.collation = options[:collation] if options.include?(:collation)
497
415
  end
498
416
  end
499
417
  end
@@ -506,16 +424,10 @@ module ActiveRecord
506
424
 
507
425
  protected
508
426
 
509
- def initialize_type_map(m)
510
- super
511
- m.register_type(/binary/i, SQLite3Binary.new)
512
- register_class_with_limit m, %r(char)i, SQLite3String
513
- end
514
-
515
427
  def table_structure(table_name)
516
- structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA').to_hash
428
+ structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA')
517
429
  raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
518
- structure
430
+ table_structure_with_collation(table_name, structure)
519
431
  end
520
432
 
521
433
  def alter_table(table_name, options = {}) #:nodoc:
@@ -550,13 +462,13 @@ module ActiveRecord
550
462
  @definition.column(column_name, column.type,
551
463
  :limit => column.limit, :default => column.default,
552
464
  :precision => column.precision, :scale => column.scale,
553
- :null => column.null)
465
+ :null => column.null, collation: column.collation)
554
466
  end
555
467
  yield @definition if block_given?
556
468
  end
557
469
  copy_table_indexes(from, to, options[:rename] || {})
558
470
  copy_table_contents(from, to,
559
- @definition.columns.map {|column| column.name},
471
+ @definition.columns.map(&:name),
560
472
  options[:rename] || {})
561
473
  end
562
474
 
@@ -569,7 +481,7 @@ module ActiveRecord
569
481
  name = name[1..-1]
570
482
  end
571
483
 
572
- to_column_names = columns(to).map { |c| c.name }
484
+ to_column_names = columns(to).map(&:name)
573
485
  columns = index.columns.map {|c| rename[c] || c }.select do |column|
574
486
  to_column_names.include?(column)
575
487
  end
@@ -586,25 +498,14 @@ module ActiveRecord
586
498
  def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
587
499
  column_mappings = Hash[columns.map {|name| [name, name]}]
588
500
  rename.each { |a| column_mappings[a.last] = a.first }
589
- from_columns = columns(from).collect {|col| col.name}
501
+ from_columns = columns(from).collect(&:name)
590
502
  columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
503
+ from_columns_to_copy = columns.map { |col| column_mappings[col] }
591
504
  quoted_columns = columns.map { |col| quote_column_name(col) } * ','
505
+ quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ','
592
506
 
593
- quoted_to = quote_table_name(to)
594
-
595
- raw_column_mappings = Hash[columns(from).map { |c| [c.name, c] }]
596
-
597
- exec_query("SELECT * FROM #{quote_table_name(from)}").each do |row|
598
- sql = "INSERT INTO #{quoted_to} (#{quoted_columns}) VALUES ("
599
-
600
- column_values = columns.map do |col|
601
- quote(row[column_mappings[col]], raw_column_mappings[col])
602
- end
603
-
604
- sql << column_values * ', '
605
- sql << ')'
606
- exec_query sql
607
- end
507
+ exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
508
+ SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
608
509
  end
609
510
 
610
511
  def sqlite_version
@@ -618,11 +519,51 @@ module ActiveRecord
618
519
  # Older versions of SQLite return:
619
520
  # column *column_name* is not unique
620
521
  when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
621
- RecordNotUnique.new(message, exception)
522
+ RecordNotUnique.new(message)
622
523
  else
623
524
  super
624
525
  end
625
526
  end
527
+
528
+ private
529
+ COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze
530
+
531
+ def table_structure_with_collation(table_name, basic_structure)
532
+ collation_hash = {}
533
+ sql = "SELECT sql FROM
534
+ (SELECT * FROM sqlite_master UNION ALL
535
+ SELECT * FROM sqlite_temp_master)
536
+ WHERE type='table' and name='#{ table_name }' \;"
537
+
538
+ # Result will have following sample string
539
+ # CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
540
+ # "password_digest" varchar COLLATE "NOCASE");
541
+ result = exec_query(sql, 'SCHEMA').first
542
+
543
+ if result
544
+ # Splitting with left parentheses and picking up last will return all
545
+ # columns separated with comma(,).
546
+ columns_string = result["sql"].split('(').last
547
+
548
+ columns_string.split(',').each do |column_string|
549
+ # This regex will match the column name and collation type and will save
550
+ # the value in $1 and $2 respectively.
551
+ collation_hash[$1] = $2 if (COLLATE_REGEX =~ column_string)
552
+ end
553
+
554
+ basic_structure.map! do |column|
555
+ column_name = column['name']
556
+
557
+ if collation_hash.has_key? column_name
558
+ column['collation'] = collation_hash[column_name]
559
+ end
560
+
561
+ column
562
+ end
563
+ else
564
+ basic_structure.to_hash
565
+ end
566
+ end
626
567
  end
627
568
  end
628
569
  end
@@ -1,38 +1,57 @@
1
1
  module ActiveRecord
2
2
  module ConnectionAdapters
3
- class StatementPool
3
+ class StatementPool # :nodoc:
4
4
  include Enumerable
5
5
 
6
- def initialize(connection, max = 1000)
7
- @connection = connection
8
- @max = max
6
+ DEFAULT_STATEMENT_LIMIT = 1000
7
+
8
+ def initialize(statement_limit = nil)
9
+ @cache = Hash.new { |h,pid| h[pid] = {} }
10
+ @statement_limit = statement_limit || DEFAULT_STATEMENT_LIMIT
9
11
  end
10
12
 
11
- def each
12
- raise NotImplementedError
13
+ def each(&block)
14
+ cache.each(&block)
13
15
  end
14
16
 
15
17
  def key?(key)
16
- raise NotImplementedError
18
+ cache.key?(key)
17
19
  end
18
20
 
19
21
  def [](key)
20
- raise NotImplementedError
22
+ cache[key]
21
23
  end
22
24
 
23
25
  def length
24
- raise NotImplementedError
26
+ cache.length
25
27
  end
26
28
 
27
- def []=(sql, key)
28
- raise NotImplementedError
29
+ def []=(sql, stmt)
30
+ while @statement_limit <= cache.size
31
+ dealloc(cache.shift.last)
32
+ end
33
+ cache[sql] = stmt
29
34
  end
30
35
 
31
36
  def clear
32
- raise NotImplementedError
37
+ cache.each_value do |stmt|
38
+ dealloc stmt
39
+ end
40
+ cache.clear
33
41
  end
34
42
 
35
43
  def delete(key)
44
+ dealloc cache[key]
45
+ cache.delete(key)
46
+ end
47
+
48
+ private
49
+
50
+ def cache
51
+ @cache[Process.pid]
52
+ end
53
+
54
+ def dealloc(stmt)
36
55
  raise NotImplementedError
37
56
  end
38
57
  end