activerecord 4.2.6 → 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 (246) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1307 -1105
  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 +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 +50 -31
  21. data/lib/active_record/associations/collection_proxy.rb +69 -29
  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 +20 -8
  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 +20 -141
  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 +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 +6 -4
  52. data/lib/active_record/attribute_set.rb +30 -3
  53. data/lib/active_record/attributes.rb +199 -80
  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 -9
  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 +378 -140
  70. data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
  71. data/lib/active_record/connection_adapters/abstract_adapter.rb +153 -59
  72. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +405 -362
  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 +25 -176
  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 -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 +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 +148 -203
  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 +3 -3
  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 +364 -109
  141. data/lib/active_record/model_schema.rb +128 -38
  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 +27 -18
  148. data/lib/active_record/railties/controller_runtime.rb +1 -1
  149. data/lib/active_record/railties/databases.rake +58 -45
  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 +80 -102
  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 -15
  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 -17
  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 +58 -41
  191. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -20
  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 -41
  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 +7 -2
  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 +58 -34
  224. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  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 -50
  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 -105
@@ -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',
@@ -74,68 +67,27 @@ module ActiveRecord
74
67
  boolean: { name: "boolean" }
75
68
  }
76
69
 
77
- class Version
78
- include Comparable
79
-
80
- def initialize(version_string)
81
- @version = version_string.split('.').map { |v| v.to_i }
82
- end
83
-
84
- def <=>(version_string)
85
- @version <=> version_string.split('.').map { |v| v.to_i }
86
- end
87
- end
88
-
89
70
  class StatementPool < ConnectionAdapters::StatementPool
90
- def initialize(connection, max)
91
- super
92
- @cache = Hash.new { |h,pid| h[pid] = {} }
93
- end
94
-
95
- def each(&block); cache.each(&block); end
96
- def key?(key); cache.key?(key); end
97
- def [](key); cache[key]; end
98
- def length; cache.length; end
99
-
100
- def []=(sql, key)
101
- while @max <= cache.size
102
- dealloc(cache.shift.last[:stmt])
103
- end
104
- cache[sql] = key
105
- end
106
-
107
- def clear
108
- cache.each_value do |hash|
109
- dealloc hash[:stmt]
110
- end
111
- cache.clear
112
- end
113
-
114
71
  private
115
- def cache
116
- @cache[$$]
117
- end
118
72
 
119
73
  def dealloc(stmt)
120
- stmt.close unless stmt.closed?
74
+ stmt[:stmt].close unless stmt[:stmt].closed?
121
75
  end
122
76
  end
123
77
 
124
- def initialize(connection, logger, connection_options, config)
125
- super(connection, logger)
78
+ def schema_creation # :nodoc:
79
+ SQLite3::SchemaCreation.new self
80
+ end
126
81
 
127
- @active = nil
128
- @statements = StatementPool.new(@connection,
129
- self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 }))
130
- @config = config
82
+ def arel_visitor # :nodoc:
83
+ Arel::Visitors::SQLite.new(self)
84
+ end
131
85
 
132
- @visitor = Arel::Visitors::SQLite.new self
86
+ def initialize(connection, logger, connection_options, config)
87
+ super(connection, logger, config)
133
88
 
134
- if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
135
- @prepared_statements = true
136
- else
137
- @prepared_statements = false
138
- end
89
+ @active = nil
90
+ @statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
139
91
  end
140
92
 
141
93
  def supports_ddl_transactions?
@@ -173,6 +125,14 @@ module ActiveRecord
173
125
  true
174
126
  end
175
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
+
176
136
  def active?
177
137
  @active != false
178
138
  end
@@ -194,6 +154,10 @@ module ActiveRecord
194
154
  true
195
155
  end
196
156
 
157
+ def valid_type?(type)
158
+ true
159
+ end
160
+
197
161
  # Returns 62. SQLite supports index names up to 64
198
162
  # characters. The rest is used by rails internally to perform
199
163
  # temporary rename operations
@@ -214,88 +178,27 @@ module ActiveRecord
214
178
  true
215
179
  end
216
180
 
217
- # QUOTING ==================================================
218
-
219
- def _quote(value) # :nodoc:
220
- case value
221
- when Type::Binary::Data
222
- "x'#{value.hex}'"
223
- else
224
- super
225
- end
226
- end
227
-
228
- def _type_cast(value) # :nodoc:
229
- case value
230
- when BigDecimal
231
- value.to_f
232
- when String
233
- if value.encoding == Encoding::ASCII_8BIT
234
- super(value.encode(Encoding::UTF_8))
235
- else
236
- super
237
- end
238
- else
239
- super
240
- end
241
- end
242
-
243
- def quote_string(s) #:nodoc:
244
- @connection.class.quote(s)
245
- end
246
-
247
- def quote_table_name_for_assignment(table, attr)
248
- quote_column_name(attr)
249
- end
250
-
251
- def quote_column_name(name) #:nodoc:
252
- %Q("#{name.to_s.gsub('"', '""')}")
253
- end
254
-
255
- # Quote date/time values for use in SQL input. Includes microseconds
256
- # if the value is a Time responding to usec.
257
- def quoted_date(value) #:nodoc:
258
- if value.respond_to?(:usec)
259
- "#{super}.#{sprintf("%06d", value.usec)}"
260
- else
261
- super
262
- end
263
- end
264
-
265
181
  #--
266
182
  # DATABASE STATEMENTS ======================================
267
183
  #++
268
184
 
269
185
  def explain(arel, binds = [])
270
186
  sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
271
- ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', []))
272
- end
273
-
274
- class ExplainPrettyPrinter
275
- # Pretty prints the result of a EXPLAIN QUERY PLAN in a way that resembles
276
- # the output of the SQLite shell:
277
- #
278
- # 0|0|0|SEARCH TABLE users USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
279
- # 0|1|1|SCAN TABLE posts (~100000 rows)
280
- #
281
- def pp(result) # :nodoc:
282
- result.rows.map do |row|
283
- row.join('|')
284
- end.join("\n") + "\n"
285
- end
187
+ SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', []))
286
188
  end
287
189
 
288
- def exec_query(sql, name = nil, binds = [])
289
- type_casted_binds = binds.map { |col, val|
290
- [col, type_cast(val, col)]
291
- }
190
+ def exec_query(sql, name = nil, binds = [], prepare: false)
191
+ type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) }
292
192
 
293
- log(sql, name, type_casted_binds) do
193
+ log(sql, name, binds) do
294
194
  # Don't cache statements if they are not prepared
295
- if without_prepared_statement?(binds)
195
+ unless prepare
296
196
  stmt = @connection.prepare(sql)
297
197
  begin
298
198
  cols = stmt.columns
199
+ unless without_prepared_statement?(binds)
200
+ stmt.bind_params(type_casted_binds)
201
+ end
299
202
  records = stmt.to_a
300
203
  ensure
301
204
  stmt.close
@@ -308,7 +211,7 @@ module ActiveRecord
308
211
  stmt = cache[:stmt]
309
212
  cols = cache[:cols] ||= stmt.columns
310
213
  stmt.reset!
311
- stmt.bind_params type_casted_binds.map { |_, val| val }
214
+ stmt.bind_params(type_casted_binds)
312
215
  end
313
216
 
314
217
  ActiveRecord::Result.new(cols, stmt.to_a)
@@ -329,26 +232,6 @@ module ActiveRecord
329
232
  log(sql, name) { @connection.execute(sql) }
330
233
  end
331
234
 
332
- def update_sql(sql, name = nil) #:nodoc:
333
- super
334
- @connection.changes
335
- end
336
-
337
- def delete_sql(sql, name = nil) #:nodoc:
338
- sql += " WHERE 1=1" unless sql =~ /WHERE/i
339
- super sql, name
340
- end
341
-
342
- def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
343
- super
344
- id_value || @connection.last_insert_row_id
345
- end
346
- alias :create :insert_sql
347
-
348
- def select_rows(sql, name = nil, binds = [])
349
- exec_query(sql, name, binds).rows
350
- end
351
-
352
235
  def begin_db_transaction #:nodoc:
353
236
  log('begin transaction',nil) { @connection.transaction }
354
237
  end
@@ -363,27 +246,61 @@ module ActiveRecord
363
246
 
364
247
  # SCHEMA STATEMENTS ========================================
365
248
 
366
- def tables(name = nil, table_name = nil) #:nodoc:
367
- sql = <<-SQL
368
- SELECT name
369
- FROM sqlite_master
370
- WHERE (type = 'table' OR type = 'view') AND NOT name = 'sqlite_sequence'
371
- SQL
372
- 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
373
255
 
374
- exec_query(sql, 'SCHEMA').map do |row|
375
- row['name']
256
+ if name
257
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
258
+ Passing arguments to #tables is deprecated without replacement.
259
+ MSG
376
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')
377
267
  end
378
- alias data_sources tables
379
268
 
380
269
  def table_exists?(table_name)
381
- 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?
382
299
  end
383
- alias data_source_exists? table_exists?
384
300
 
385
301
  # Returns an array of +Column+ objects for the table specified by +table_name+.
386
- def columns(table_name) #:nodoc:
302
+ def columns(table_name) # :nodoc:
303
+ table_name = table_name.to_s
387
304
  table_structure(table_name).map do |field|
388
305
  case field["dflt_value"]
389
306
  when /^null$/i
@@ -394,9 +311,10 @@ module ActiveRecord
394
311
  field["dflt_value"] = $1.gsub('""', '"')
395
312
  end
396
313
 
314
+ collation = field['collation']
397
315
  sql_type = field['type']
398
- cast_type = lookup_cast_type(sql_type)
399
- 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)
400
318
  end
401
319
  end
402
320
 
@@ -425,13 +343,13 @@ module ActiveRecord
425
343
  end
426
344
  end
427
345
 
428
- def primary_key(table_name) #:nodoc:
346
+ def primary_keys(table_name) # :nodoc:
429
347
  pks = table_structure(table_name).select { |f| f['pk'] > 0 }
430
- return nil unless pks.count == 1
431
- pks[0]['name']
348
+ pks.sort_by { |f| f['pk'] }.map { |f| f['name'] }
432
349
  end
433
350
 
434
- 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)
435
353
  exec_query "DROP INDEX #{quote_column_name(index_name)}"
436
354
  end
437
355
 
@@ -466,13 +384,15 @@ module ActiveRecord
466
384
  end
467
385
  end
468
386
 
469
- 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
+
470
390
  alter_table(table_name) do |definition|
471
391
  definition[column_name].default = default
472
392
  end
473
393
  end
474
394
 
475
- def change_column_null(table_name, column_name, null, default = nil)
395
+ def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
476
396
  unless null || default.nil?
477
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")
478
398
  end
@@ -491,6 +411,7 @@ module ActiveRecord
491
411
  self.null = options[:null] if options.include?(:null)
492
412
  self.precision = options[:precision] if options.include?(:precision)
493
413
  self.scale = options[:scale] if options.include?(:scale)
414
+ self.collation = options[:collation] if options.include?(:collation)
494
415
  end
495
416
  end
496
417
  end
@@ -503,15 +424,10 @@ module ActiveRecord
503
424
 
504
425
  protected
505
426
 
506
- def initialize_type_map(m)
507
- super
508
- m.register_type(/binary/i, SQLite3Binary.new)
509
- end
510
-
511
427
  def table_structure(table_name)
512
- 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')
513
429
  raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
514
- structure
430
+ table_structure_with_collation(table_name, structure)
515
431
  end
516
432
 
517
433
  def alter_table(table_name, options = {}) #:nodoc:
@@ -546,13 +462,13 @@ module ActiveRecord
546
462
  @definition.column(column_name, column.type,
547
463
  :limit => column.limit, :default => column.default,
548
464
  :precision => column.precision, :scale => column.scale,
549
- :null => column.null)
465
+ :null => column.null, collation: column.collation)
550
466
  end
551
467
  yield @definition if block_given?
552
468
  end
553
469
  copy_table_indexes(from, to, options[:rename] || {})
554
470
  copy_table_contents(from, to,
555
- @definition.columns.map {|column| column.name},
471
+ @definition.columns.map(&:name),
556
472
  options[:rename] || {})
557
473
  end
558
474
 
@@ -565,7 +481,7 @@ module ActiveRecord
565
481
  name = name[1..-1]
566
482
  end
567
483
 
568
- to_column_names = columns(to).map { |c| c.name }
484
+ to_column_names = columns(to).map(&:name)
569
485
  columns = index.columns.map {|c| rename[c] || c }.select do |column|
570
486
  to_column_names.include?(column)
571
487
  end
@@ -582,25 +498,14 @@ module ActiveRecord
582
498
  def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
583
499
  column_mappings = Hash[columns.map {|name| [name, name]}]
584
500
  rename.each { |a| column_mappings[a.last] = a.first }
585
- from_columns = columns(from).collect {|col| col.name}
501
+ from_columns = columns(from).collect(&:name)
586
502
  columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
503
+ from_columns_to_copy = columns.map { |col| column_mappings[col] }
587
504
  quoted_columns = columns.map { |col| quote_column_name(col) } * ','
505
+ quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ','
588
506
 
589
- quoted_to = quote_table_name(to)
590
-
591
- raw_column_mappings = Hash[columns(from).map { |c| [c.name, c] }]
592
-
593
- exec_query("SELECT * FROM #{quote_table_name(from)}").each do |row|
594
- sql = "INSERT INTO #{quoted_to} (#{quoted_columns}) VALUES ("
595
-
596
- column_values = columns.map do |col|
597
- quote(row[column_mappings[col]], raw_column_mappings[col])
598
- end
599
-
600
- sql << column_values * ', '
601
- sql << ')'
602
- exec_query sql
603
- end
507
+ exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
508
+ SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
604
509
  end
605
510
 
606
511
  def sqlite_version
@@ -614,11 +519,51 @@ module ActiveRecord
614
519
  # Older versions of SQLite return:
615
520
  # column *column_name* is not unique
616
521
  when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
617
- RecordNotUnique.new(message, exception)
522
+ RecordNotUnique.new(message)
618
523
  else
619
524
  super
620
525
  end
621
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
622
567
  end
623
568
  end
624
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