activerecord 4.2.11.3 → 5.0.0.1

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 (246) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1281 -1204
  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 +35 -24
  8. data/lib/active_record/association_relation.rb +3 -3
  9. data/lib/active_record/associations/alias_tracker.rb +19 -16
  10. data/lib/active_record/associations/association.rb +11 -9
  11. data/lib/active_record/associations/association_scope.rb +73 -102
  12. data/lib/active_record/associations/belongs_to_association.rb +21 -32
  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 +7 -19
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +14 -11
  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 +49 -41
  21. data/lib/active_record/associations/collection_proxy.rb +67 -27
  22. data/lib/active_record/associations/foreign_association.rb +1 -1
  23. data/lib/active_record/associations/has_many_association.rb +20 -71
  24. data/lib/active_record/associations/has_many_through_association.rb +8 -47
  25. data/lib/active_record/associations/has_one_association.rb +12 -5
  26. data/lib/active_record/associations/join_dependency/join_association.rb +16 -10
  27. data/lib/active_record/associations/join_dependency.rb +29 -19
  28. data/lib/active_record/associations/preloader/association.rb +46 -52
  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 +14 -4
  34. data/lib/active_record/associations/singular_association.rb +7 -1
  35. data/lib/active_record/associations/through_association.rb +11 -3
  36. data/lib/active_record/associations.rb +317 -209
  37. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  38. data/lib/active_record/attribute.rb +68 -18
  39. data/lib/active_record/attribute_assignment.rb +19 -140
  40. data/lib/active_record/attribute_decorators.rb +6 -5
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
  42. data/lib/active_record/attribute_methods/dirty.rb +46 -86
  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 +61 -14
  48. data/lib/active_record/attribute_methods/write.rb +13 -37
  49. data/lib/active_record/attribute_methods.rb +76 -47
  50. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  51. data/lib/active_record/attribute_set/builder.rb +6 -4
  52. data/lib/active_record/attribute_set.rb +30 -3
  53. data/lib/active_record/attributes.rb +199 -81
  54. data/lib/active_record/autosave_association.rb +49 -16
  55. data/lib/active_record/base.rb +32 -23
  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 +452 -182
  61. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  62. data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -61
  63. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -2
  64. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -10
  65. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  66. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
  67. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -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 +380 -141
  70. data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
  71. data/lib/active_record/connection_adapters/abstract_adapter.rb +141 -59
  72. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +401 -370
  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 +29 -166
  85. data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
  86. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +10 -72
  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 -57
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -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 -22
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  95. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
  97. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  98. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
  100. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
  101. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  102. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  103. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  105. data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
  106. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  107. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  108. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  109. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +234 -148
  110. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  111. data/lib/active_record/connection_adapters/postgresql_adapter.rb +248 -160
  112. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  113. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  114. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  115. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  116. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  117. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +149 -192
  118. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  119. data/lib/active_record/connection_handling.rb +37 -14
  120. data/lib/active_record/core.rb +89 -107
  121. data/lib/active_record/counter_cache.rb +13 -24
  122. data/lib/active_record/dynamic_matchers.rb +1 -20
  123. data/lib/active_record/enum.rb +113 -76
  124. data/lib/active_record/errors.rb +87 -48
  125. data/lib/active_record/explain_registry.rb +1 -1
  126. data/lib/active_record/explain_subscriber.rb +1 -1
  127. data/lib/active_record/fixture_set/file.rb +26 -5
  128. data/lib/active_record/fixtures.rb +76 -40
  129. data/lib/active_record/gem_version.rb +4 -4
  130. data/lib/active_record/inheritance.rb +32 -40
  131. data/lib/active_record/integration.rb +4 -4
  132. data/lib/active_record/internal_metadata.rb +56 -0
  133. data/lib/active_record/legacy_yaml_adapter.rb +18 -2
  134. data/lib/active_record/locale/en.yml +3 -2
  135. data/lib/active_record/locking/optimistic.rb +15 -15
  136. data/lib/active_record/locking/pessimistic.rb +1 -1
  137. data/lib/active_record/log_subscriber.rb +43 -21
  138. data/lib/active_record/migration/command_recorder.rb +59 -18
  139. data/lib/active_record/migration/compatibility.rb +126 -0
  140. data/lib/active_record/migration.rb +363 -133
  141. data/lib/active_record/model_schema.rb +129 -41
  142. data/lib/active_record/nested_attributes.rb +58 -29
  143. data/lib/active_record/null_relation.rb +16 -8
  144. data/lib/active_record/persistence.rb +121 -80
  145. data/lib/active_record/query_cache.rb +15 -18
  146. data/lib/active_record/querying.rb +10 -9
  147. data/lib/active_record/railtie.rb +23 -16
  148. data/lib/active_record/railties/controller_runtime.rb +1 -1
  149. data/lib/active_record/railties/databases.rake +69 -46
  150. data/lib/active_record/readonly_attributes.rb +1 -1
  151. data/lib/active_record/reflection.rb +282 -115
  152. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  153. data/lib/active_record/relation/batches.rb +139 -34
  154. data/lib/active_record/relation/calculations.rb +79 -108
  155. data/lib/active_record/relation/delegation.rb +7 -20
  156. data/lib/active_record/relation/finder_methods.rb +163 -81
  157. data/lib/active_record/relation/from_clause.rb +32 -0
  158. data/lib/active_record/relation/merger.rb +16 -42
  159. data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
  160. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  161. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  162. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  163. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  164. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  165. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  166. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  167. data/lib/active_record/relation/predicate_builder.rb +120 -107
  168. data/lib/active_record/relation/query_attribute.rb +19 -0
  169. data/lib/active_record/relation/query_methods.rb +308 -244
  170. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  171. data/lib/active_record/relation/spawn_methods.rb +4 -7
  172. data/lib/active_record/relation/where_clause.rb +174 -0
  173. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  174. data/lib/active_record/relation.rb +176 -116
  175. data/lib/active_record/result.rb +4 -3
  176. data/lib/active_record/runtime_registry.rb +1 -1
  177. data/lib/active_record/sanitization.rb +95 -66
  178. data/lib/active_record/schema.rb +26 -22
  179. data/lib/active_record/schema_dumper.rb +62 -38
  180. data/lib/active_record/schema_migration.rb +11 -14
  181. data/lib/active_record/scoping/default.rb +23 -9
  182. data/lib/active_record/scoping/named.rb +49 -28
  183. data/lib/active_record/scoping.rb +32 -15
  184. data/lib/active_record/secure_token.rb +38 -0
  185. data/lib/active_record/serialization.rb +2 -4
  186. data/lib/active_record/statement_cache.rb +16 -14
  187. data/lib/active_record/store.rb +8 -3
  188. data/lib/active_record/suppressor.rb +58 -0
  189. data/lib/active_record/table_metadata.rb +68 -0
  190. data/lib/active_record/tasks/database_tasks.rb +57 -43
  191. data/lib/active_record/tasks/mysql_database_tasks.rb +6 -14
  192. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
  193. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  194. data/lib/active_record/timestamp.rb +20 -9
  195. data/lib/active_record/touch_later.rb +58 -0
  196. data/lib/active_record/transactions.rb +138 -56
  197. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  198. data/lib/active_record/type/date.rb +2 -45
  199. data/lib/active_record/type/date_time.rb +2 -49
  200. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  201. data/lib/active_record/type/internal/timezone.rb +15 -0
  202. data/lib/active_record/type/serialized.rb +15 -14
  203. data/lib/active_record/type/time.rb +10 -16
  204. data/lib/active_record/type/type_map.rb +4 -4
  205. data/lib/active_record/type.rb +66 -17
  206. data/lib/active_record/type_caster/connection.rb +29 -0
  207. data/lib/active_record/type_caster/map.rb +19 -0
  208. data/lib/active_record/type_caster.rb +7 -0
  209. data/lib/active_record/validations/absence.rb +23 -0
  210. data/lib/active_record/validations/associated.rb +10 -3
  211. data/lib/active_record/validations/length.rb +24 -0
  212. data/lib/active_record/validations/presence.rb +11 -12
  213. data/lib/active_record/validations/uniqueness.rb +30 -29
  214. data/lib/active_record/validations.rb +33 -32
  215. data/lib/active_record.rb +8 -4
  216. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  217. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
  218. data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -1
  219. data/lib/rails/generators/active_record/migration.rb +7 -0
  220. data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
  221. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  222. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  223. metadata +60 -34
  224. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  225. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  226. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  227. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  228. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  229. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  230. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  231. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  232. data/lib/active_record/type/big_integer.rb +0 -13
  233. data/lib/active_record/type/binary.rb +0 -50
  234. data/lib/active_record/type/boolean.rb +0 -31
  235. data/lib/active_record/type/decimal.rb +0 -64
  236. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  237. data/lib/active_record/type/decorator.rb +0 -14
  238. data/lib/active_record/type/float.rb +0 -19
  239. data/lib/active_record/type/integer.rb +0 -59
  240. data/lib/active_record/type/mutable.rb +0 -16
  241. data/lib/active_record/type/numeric.rb +0 -36
  242. data/lib/active_record/type/string.rb +0 -40
  243. data/lib/active_record/type/text.rb +0 -11
  244. data/lib/active_record/type/time_value.rb +0 -38
  245. data/lib/active_record/type/unsigned_integer.rb +0 -15
  246. data/lib/active_record/type/value.rb +0 -110
@@ -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,15 +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
45
  # The SQLite3 adapter works SQLite 3.6.16 or newer
54
46
  # with the sqlite3-ruby drivers (available as gem from https://rubygems.org/gems/sqlite3).
55
47
  #
@@ -58,7 +50,8 @@ module ActiveRecord
58
50
  # * <tt>:database</tt> - Path to the database file.
59
51
  class SQLite3Adapter < AbstractAdapter
60
52
  ADAPTER_NAME = 'SQLite'.freeze
61
- include Savepoints
53
+
54
+ include SQLite3::Quoting
62
55
 
63
56
  NATIVE_DATABASE_TYPES = {
64
57
  primary_key: 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL',
@@ -75,55 +68,26 @@ module ActiveRecord
75
68
  }
76
69
 
77
70
  class StatementPool < ConnectionAdapters::StatementPool
78
- def initialize(connection, max)
79
- super
80
- @cache = Hash.new { |h,pid| h[pid] = {} }
81
- end
82
-
83
- def each(&block); cache.each(&block); end
84
- def key?(key); cache.key?(key); end
85
- def [](key); cache[key]; end
86
- def length; cache.length; end
87
-
88
- def []=(sql, key)
89
- while @max <= cache.size
90
- dealloc(cache.shift.last[:stmt])
91
- end
92
- cache[sql] = key
93
- end
94
-
95
- def clear
96
- cache.each_value do |hash|
97
- dealloc hash[:stmt]
98
- end
99
- cache.clear
100
- end
101
-
102
71
  private
103
- def cache
104
- @cache[$$]
105
- end
106
72
 
107
73
  def dealloc(stmt)
108
- stmt.close unless stmt.closed?
74
+ stmt[:stmt].close unless stmt[:stmt].closed?
109
75
  end
110
76
  end
111
77
 
112
- def initialize(connection, logger, connection_options, config)
113
- super(connection, logger)
78
+ def schema_creation # :nodoc:
79
+ SQLite3::SchemaCreation.new self
80
+ end
114
81
 
115
- @active = nil
116
- @statements = StatementPool.new(@connection,
117
- self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 }))
118
- @config = config
82
+ def arel_visitor # :nodoc:
83
+ Arel::Visitors::SQLite.new(self)
84
+ end
119
85
 
120
- @visitor = Arel::Visitors::SQLite.new self
86
+ def initialize(connection, logger, connection_options, config)
87
+ super(connection, logger, config)
121
88
 
122
- if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
123
- @prepared_statements = true
124
- else
125
- @prepared_statements = false
126
- end
89
+ @active = nil
90
+ @statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
127
91
  end
128
92
 
129
93
  def supports_ddl_transactions?
@@ -161,6 +125,14 @@ module ActiveRecord
161
125
  true
162
126
  end
163
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
+
164
136
  def active?
165
137
  @active != false
166
138
  end
@@ -182,6 +154,10 @@ module ActiveRecord
182
154
  true
183
155
  end
184
156
 
157
+ def valid_type?(type)
158
+ true
159
+ end
160
+
185
161
  # Returns 62. SQLite supports index names up to 64
186
162
  # characters. The rest is used by rails internally to perform
187
163
  # temporary rename operations
@@ -202,88 +178,27 @@ module ActiveRecord
202
178
  true
203
179
  end
204
180
 
205
- # QUOTING ==================================================
206
-
207
- def _quote(value) # :nodoc:
208
- case value
209
- when Type::Binary::Data
210
- "x'#{value.hex}'"
211
- else
212
- super
213
- end
214
- end
215
-
216
- def _type_cast(value) # :nodoc:
217
- case value
218
- when BigDecimal
219
- value.to_f
220
- when String
221
- if value.encoding == Encoding::ASCII_8BIT
222
- super(value.encode(Encoding::UTF_8))
223
- else
224
- super
225
- end
226
- else
227
- super
228
- end
229
- end
230
-
231
- def quote_string(s) #:nodoc:
232
- @connection.class.quote(s)
233
- end
234
-
235
- def quote_table_name_for_assignment(table, attr)
236
- quote_column_name(attr)
237
- end
238
-
239
- def quote_column_name(name) #:nodoc:
240
- %Q("#{name.to_s.gsub('"', '""')}")
241
- end
242
-
243
- # Quote date/time values for use in SQL input. Includes microseconds
244
- # if the value is a Time responding to usec.
245
- def quoted_date(value) #:nodoc:
246
- if value.respond_to?(:usec)
247
- "#{super}.#{sprintf("%06d", value.usec)}"
248
- else
249
- super
250
- end
251
- end
252
-
253
181
  #--
254
182
  # DATABASE STATEMENTS ======================================
255
183
  #++
256
184
 
257
185
  def explain(arel, binds = [])
258
186
  sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
259
- ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', []))
260
- end
261
-
262
- class ExplainPrettyPrinter
263
- # Pretty prints the result of a EXPLAIN QUERY PLAN in a way that resembles
264
- # the output of the SQLite shell:
265
- #
266
- # 0|0|0|SEARCH TABLE users USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
267
- # 0|1|1|SCAN TABLE posts (~100000 rows)
268
- #
269
- def pp(result) # :nodoc:
270
- result.rows.map do |row|
271
- row.join('|')
272
- end.join("\n") + "\n"
273
- end
187
+ SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', []))
274
188
  end
275
189
 
276
- def exec_query(sql, name = nil, binds = [])
277
- type_casted_binds = binds.map { |col, val|
278
- [col, type_cast(val, col)]
279
- }
190
+ def exec_query(sql, name = nil, binds = [], prepare: false)
191
+ type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) }
280
192
 
281
- log(sql, name, type_casted_binds) do
193
+ log(sql, name, binds) do
282
194
  # Don't cache statements if they are not prepared
283
- if without_prepared_statement?(binds)
195
+ unless prepare
284
196
  stmt = @connection.prepare(sql)
285
197
  begin
286
198
  cols = stmt.columns
199
+ unless without_prepared_statement?(binds)
200
+ stmt.bind_params(type_casted_binds)
201
+ end
287
202
  records = stmt.to_a
288
203
  ensure
289
204
  stmt.close
@@ -296,7 +211,7 @@ module ActiveRecord
296
211
  stmt = cache[:stmt]
297
212
  cols = cache[:cols] ||= stmt.columns
298
213
  stmt.reset!
299
- stmt.bind_params type_casted_binds.map { |_, val| val }
214
+ stmt.bind_params(type_casted_binds)
300
215
  end
301
216
 
302
217
  ActiveRecord::Result.new(cols, stmt.to_a)
@@ -317,26 +232,6 @@ module ActiveRecord
317
232
  log(sql, name) { @connection.execute(sql) }
318
233
  end
319
234
 
320
- def update_sql(sql, name = nil) #:nodoc:
321
- super
322
- @connection.changes
323
- end
324
-
325
- def delete_sql(sql, name = nil) #:nodoc:
326
- sql += " WHERE 1=1" unless sql =~ /WHERE/i
327
- super sql, name
328
- end
329
-
330
- def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
331
- super
332
- id_value || @connection.last_insert_row_id
333
- end
334
- alias :create :insert_sql
335
-
336
- def select_rows(sql, name = nil, binds = [])
337
- exec_query(sql, name, binds).rows
338
- end
339
-
340
235
  def begin_db_transaction #:nodoc:
341
236
  log('begin transaction',nil) { @connection.transaction }
342
237
  end
@@ -351,27 +246,61 @@ module ActiveRecord
351
246
 
352
247
  # SCHEMA STATEMENTS ========================================
353
248
 
354
- def tables(name = nil, table_name = nil) #:nodoc:
355
- sql = <<-SQL
356
- SELECT name
357
- FROM sqlite_master
358
- WHERE (type = 'table' OR type = 'view') AND NOT name = 'sqlite_sequence'
359
- SQL
360
- sql << " AND name = #{quote_table_name(table_name)}" if table_name
361
-
362
- exec_query(sql, 'SCHEMA').map do |row|
363
- row['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
255
+
256
+ if name
257
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
258
+ Passing arguments to #tables is deprecated without replacement.
259
+ MSG
364
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')
365
267
  end
366
- alias data_sources tables
367
268
 
368
269
  def table_exists?(table_name)
369
- 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?
370
299
  end
371
- alias data_source_exists? table_exists?
372
300
 
373
301
  # Returns an array of +Column+ objects for the table specified by +table_name+.
374
- def columns(table_name) #:nodoc:
302
+ def columns(table_name) # :nodoc:
303
+ table_name = table_name.to_s
375
304
  table_structure(table_name).map do |field|
376
305
  case field["dflt_value"]
377
306
  when /^null$/i
@@ -382,9 +311,10 @@ module ActiveRecord
382
311
  field["dflt_value"] = $1.gsub('""', '"')
383
312
  end
384
313
 
314
+ collation = field['collation']
385
315
  sql_type = field['type']
386
- cast_type = lookup_cast_type(sql_type)
387
- 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)
388
318
  end
389
319
  end
390
320
 
@@ -413,13 +343,13 @@ module ActiveRecord
413
343
  end
414
344
  end
415
345
 
416
- def primary_key(table_name) #:nodoc:
346
+ def primary_keys(table_name) # :nodoc:
417
347
  pks = table_structure(table_name).select { |f| f['pk'] > 0 }
418
- return nil unless pks.count == 1
419
- pks[0]['name']
348
+ pks.sort_by { |f| f['pk'] }.map { |f| f['name'] }
420
349
  end
421
350
 
422
- 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)
423
353
  exec_query "DROP INDEX #{quote_column_name(index_name)}"
424
354
  end
425
355
 
@@ -454,13 +384,15 @@ module ActiveRecord
454
384
  end
455
385
  end
456
386
 
457
- 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
+
458
390
  alter_table(table_name) do |definition|
459
391
  definition[column_name].default = default
460
392
  end
461
393
  end
462
394
 
463
- def change_column_null(table_name, column_name, null, default = nil)
395
+ def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
464
396
  unless null || default.nil?
465
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")
466
398
  end
@@ -479,6 +411,7 @@ module ActiveRecord
479
411
  self.null = options[:null] if options.include?(:null)
480
412
  self.precision = options[:precision] if options.include?(:precision)
481
413
  self.scale = options[:scale] if options.include?(:scale)
414
+ self.collation = options[:collation] if options.include?(:collation)
482
415
  end
483
416
  end
484
417
  end
@@ -491,15 +424,10 @@ module ActiveRecord
491
424
 
492
425
  protected
493
426
 
494
- def initialize_type_map(m)
495
- super
496
- m.register_type(/binary/i, SQLite3Binary.new)
497
- end
498
-
499
427
  def table_structure(table_name)
500
- 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')
501
429
  raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
502
- structure
430
+ table_structure_with_collation(table_name, structure)
503
431
  end
504
432
 
505
433
  def alter_table(table_name, options = {}) #:nodoc:
@@ -534,13 +462,13 @@ module ActiveRecord
534
462
  @definition.column(column_name, column.type,
535
463
  :limit => column.limit, :default => column.default,
536
464
  :precision => column.precision, :scale => column.scale,
537
- :null => column.null)
465
+ :null => column.null, collation: column.collation)
538
466
  end
539
467
  yield @definition if block_given?
540
468
  end
541
469
  copy_table_indexes(from, to, options[:rename] || {})
542
470
  copy_table_contents(from, to,
543
- @definition.columns.map {|column| column.name},
471
+ @definition.columns.map(&:name),
544
472
  options[:rename] || {})
545
473
  end
546
474
 
@@ -553,7 +481,7 @@ module ActiveRecord
553
481
  name = name[1..-1]
554
482
  end
555
483
 
556
- to_column_names = columns(to).map { |c| c.name }
484
+ to_column_names = columns(to).map(&:name)
557
485
  columns = index.columns.map {|c| rename[c] || c }.select do |column|
558
486
  to_column_names.include?(column)
559
487
  end
@@ -570,25 +498,14 @@ module ActiveRecord
570
498
  def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
571
499
  column_mappings = Hash[columns.map {|name| [name, name]}]
572
500
  rename.each { |a| column_mappings[a.last] = a.first }
573
- from_columns = columns(from).collect {|col| col.name}
501
+ from_columns = columns(from).collect(&:name)
574
502
  columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
503
+ from_columns_to_copy = columns.map { |col| column_mappings[col] }
575
504
  quoted_columns = columns.map { |col| quote_column_name(col) } * ','
505
+ quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ','
576
506
 
577
- quoted_to = quote_table_name(to)
578
-
579
- raw_column_mappings = Hash[columns(from).map { |c| [c.name, c] }]
580
-
581
- exec_query("SELECT * FROM #{quote_table_name(from)}").each do |row|
582
- sql = "INSERT INTO #{quoted_to} (#{quoted_columns}) VALUES ("
583
-
584
- column_values = columns.map do |col|
585
- quote(row[column_mappings[col]], raw_column_mappings[col])
586
- end
587
-
588
- sql << column_values * ', '
589
- sql << ')'
590
- exec_query sql
591
- end
507
+ exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
508
+ SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
592
509
  end
593
510
 
594
511
  def sqlite_version
@@ -602,11 +519,51 @@ module ActiveRecord
602
519
  # Older versions of SQLite return:
603
520
  # column *column_name* is not unique
604
521
  when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
605
- RecordNotUnique.new(message, exception)
522
+ RecordNotUnique.new(message)
606
523
  else
607
524
  super
608
525
  end
609
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
610
567
  end
611
568
  end
612
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