activerecord 5.2.3

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 (244) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +937 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +217 -0
  5. data/examples/performance.rb +185 -0
  6. data/examples/simple.rb +15 -0
  7. data/lib/active_record.rb +188 -0
  8. data/lib/active_record/aggregations.rb +283 -0
  9. data/lib/active_record/association_relation.rb +40 -0
  10. data/lib/active_record/associations.rb +1860 -0
  11. data/lib/active_record/associations/alias_tracker.rb +81 -0
  12. data/lib/active_record/associations/association.rb +299 -0
  13. data/lib/active_record/associations/association_scope.rb +168 -0
  14. data/lib/active_record/associations/belongs_to_association.rb +130 -0
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +40 -0
  16. data/lib/active_record/associations/builder/association.rb +140 -0
  17. data/lib/active_record/associations/builder/belongs_to.rb +163 -0
  18. data/lib/active_record/associations/builder/collection_association.rb +82 -0
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +135 -0
  20. data/lib/active_record/associations/builder/has_many.rb +17 -0
  21. data/lib/active_record/associations/builder/has_one.rb +30 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +42 -0
  23. data/lib/active_record/associations/collection_association.rb +513 -0
  24. data/lib/active_record/associations/collection_proxy.rb +1131 -0
  25. data/lib/active_record/associations/foreign_association.rb +13 -0
  26. data/lib/active_record/associations/has_many_association.rb +144 -0
  27. data/lib/active_record/associations/has_many_through_association.rb +227 -0
  28. data/lib/active_record/associations/has_one_association.rb +120 -0
  29. data/lib/active_record/associations/has_one_through_association.rb +45 -0
  30. data/lib/active_record/associations/join_dependency.rb +262 -0
  31. data/lib/active_record/associations/join_dependency/join_association.rb +60 -0
  32. data/lib/active_record/associations/join_dependency/join_base.rb +23 -0
  33. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  34. data/lib/active_record/associations/preloader.rb +193 -0
  35. data/lib/active_record/associations/preloader/association.rb +131 -0
  36. data/lib/active_record/associations/preloader/through_association.rb +107 -0
  37. data/lib/active_record/associations/singular_association.rb +73 -0
  38. data/lib/active_record/associations/through_association.rb +121 -0
  39. data/lib/active_record/attribute_assignment.rb +88 -0
  40. data/lib/active_record/attribute_decorators.rb +90 -0
  41. data/lib/active_record/attribute_methods.rb +492 -0
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +78 -0
  43. data/lib/active_record/attribute_methods/dirty.rb +150 -0
  44. data/lib/active_record/attribute_methods/primary_key.rb +143 -0
  45. data/lib/active_record/attribute_methods/query.rb +42 -0
  46. data/lib/active_record/attribute_methods/read.rb +85 -0
  47. data/lib/active_record/attribute_methods/serialization.rb +90 -0
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +91 -0
  49. data/lib/active_record/attribute_methods/write.rb +68 -0
  50. data/lib/active_record/attributes.rb +266 -0
  51. data/lib/active_record/autosave_association.rb +498 -0
  52. data/lib/active_record/base.rb +329 -0
  53. data/lib/active_record/callbacks.rb +353 -0
  54. data/lib/active_record/coders/json.rb +15 -0
  55. data/lib/active_record/coders/yaml_column.rb +50 -0
  56. data/lib/active_record/collection_cache_key.rb +53 -0
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1068 -0
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +72 -0
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +540 -0
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +145 -0
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +200 -0
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +146 -0
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +685 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +95 -0
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1396 -0
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +283 -0
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +628 -0
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +887 -0
  70. data/lib/active_record/connection_adapters/column.rb +91 -0
  71. data/lib/active_record/connection_adapters/connection_specification.rb +287 -0
  72. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
  73. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  74. data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
  75. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  76. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
  81. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
  82. data/lib/active_record/connection_adapters/mysql2_adapter.rb +129 -0
  83. data/lib/active_record/connection_adapters/postgresql/column.rb +44 -0
  84. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +163 -0
  85. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +56 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +111 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  109. data/lib/active_record/connection_adapters/postgresql/quoting.rb +168 -0
  110. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +206 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  114. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +774 -0
  115. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  116. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  117. data/lib/active_record/connection_adapters/postgresql_adapter.rb +863 -0
  118. data/lib/active_record/connection_adapters/schema_cache.rb +118 -0
  119. data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +573 -0
  127. data/lib/active_record/connection_adapters/statement_pool.rb +61 -0
  128. data/lib/active_record/connection_handling.rb +145 -0
  129. data/lib/active_record/core.rb +559 -0
  130. data/lib/active_record/counter_cache.rb +218 -0
  131. data/lib/active_record/define_callbacks.rb +22 -0
  132. data/lib/active_record/dynamic_matchers.rb +122 -0
  133. data/lib/active_record/enum.rb +244 -0
  134. data/lib/active_record/errors.rb +380 -0
  135. data/lib/active_record/explain.rb +50 -0
  136. data/lib/active_record/explain_registry.rb +32 -0
  137. data/lib/active_record/explain_subscriber.rb +34 -0
  138. data/lib/active_record/fixture_set/file.rb +82 -0
  139. data/lib/active_record/fixtures.rb +1065 -0
  140. data/lib/active_record/gem_version.rb +17 -0
  141. data/lib/active_record/inheritance.rb +283 -0
  142. data/lib/active_record/integration.rb +155 -0
  143. data/lib/active_record/internal_metadata.rb +45 -0
  144. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  145. data/lib/active_record/locale/en.yml +48 -0
  146. data/lib/active_record/locking/optimistic.rb +198 -0
  147. data/lib/active_record/locking/pessimistic.rb +89 -0
  148. data/lib/active_record/log_subscriber.rb +137 -0
  149. data/lib/active_record/migration.rb +1378 -0
  150. data/lib/active_record/migration/command_recorder.rb +240 -0
  151. data/lib/active_record/migration/compatibility.rb +217 -0
  152. data/lib/active_record/migration/join_table.rb +17 -0
  153. data/lib/active_record/model_schema.rb +521 -0
  154. data/lib/active_record/nested_attributes.rb +600 -0
  155. data/lib/active_record/no_touching.rb +58 -0
  156. data/lib/active_record/null_relation.rb +68 -0
  157. data/lib/active_record/persistence.rb +763 -0
  158. data/lib/active_record/query_cache.rb +45 -0
  159. data/lib/active_record/querying.rb +70 -0
  160. data/lib/active_record/railtie.rb +226 -0
  161. data/lib/active_record/railties/console_sandbox.rb +7 -0
  162. data/lib/active_record/railties/controller_runtime.rb +56 -0
  163. data/lib/active_record/railties/databases.rake +377 -0
  164. data/lib/active_record/readonly_attributes.rb +24 -0
  165. data/lib/active_record/reflection.rb +1044 -0
  166. data/lib/active_record/relation.rb +629 -0
  167. data/lib/active_record/relation/batches.rb +287 -0
  168. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  169. data/lib/active_record/relation/calculations.rb +417 -0
  170. data/lib/active_record/relation/delegation.rb +147 -0
  171. data/lib/active_record/relation/finder_methods.rb +565 -0
  172. data/lib/active_record/relation/from_clause.rb +26 -0
  173. data/lib/active_record/relation/merger.rb +193 -0
  174. data/lib/active_record/relation/predicate_builder.rb +152 -0
  175. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  176. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  177. data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
  178. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
  179. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  180. data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
  181. data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
  182. data/lib/active_record/relation/query_attribute.rb +45 -0
  183. data/lib/active_record/relation/query_methods.rb +1231 -0
  184. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  185. data/lib/active_record/relation/spawn_methods.rb +77 -0
  186. data/lib/active_record/relation/where_clause.rb +186 -0
  187. data/lib/active_record/relation/where_clause_factory.rb +34 -0
  188. data/lib/active_record/result.rb +149 -0
  189. data/lib/active_record/runtime_registry.rb +24 -0
  190. data/lib/active_record/sanitization.rb +222 -0
  191. data/lib/active_record/schema.rb +70 -0
  192. data/lib/active_record/schema_dumper.rb +255 -0
  193. data/lib/active_record/schema_migration.rb +56 -0
  194. data/lib/active_record/scoping.rb +106 -0
  195. data/lib/active_record/scoping/default.rb +152 -0
  196. data/lib/active_record/scoping/named.rb +213 -0
  197. data/lib/active_record/secure_token.rb +40 -0
  198. data/lib/active_record/serialization.rb +22 -0
  199. data/lib/active_record/statement_cache.rb +121 -0
  200. data/lib/active_record/store.rb +211 -0
  201. data/lib/active_record/suppressor.rb +61 -0
  202. data/lib/active_record/table_metadata.rb +82 -0
  203. data/lib/active_record/tasks/database_tasks.rb +337 -0
  204. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  205. data/lib/active_record/tasks/postgresql_database_tasks.rb +143 -0
  206. data/lib/active_record/tasks/sqlite_database_tasks.rb +83 -0
  207. data/lib/active_record/timestamp.rb +153 -0
  208. data/lib/active_record/touch_later.rb +64 -0
  209. data/lib/active_record/transactions.rb +502 -0
  210. data/lib/active_record/translation.rb +24 -0
  211. data/lib/active_record/type.rb +79 -0
  212. data/lib/active_record/type/adapter_specific_registry.rb +136 -0
  213. data/lib/active_record/type/date.rb +9 -0
  214. data/lib/active_record/type/date_time.rb +9 -0
  215. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  216. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  217. data/lib/active_record/type/internal/timezone.rb +17 -0
  218. data/lib/active_record/type/json.rb +30 -0
  219. data/lib/active_record/type/serialized.rb +71 -0
  220. data/lib/active_record/type/text.rb +11 -0
  221. data/lib/active_record/type/time.rb +21 -0
  222. data/lib/active_record/type/type_map.rb +62 -0
  223. data/lib/active_record/type/unsigned_integer.rb +17 -0
  224. data/lib/active_record/type_caster.rb +9 -0
  225. data/lib/active_record/type_caster/connection.rb +33 -0
  226. data/lib/active_record/type_caster/map.rb +23 -0
  227. data/lib/active_record/validations.rb +93 -0
  228. data/lib/active_record/validations/absence.rb +25 -0
  229. data/lib/active_record/validations/associated.rb +60 -0
  230. data/lib/active_record/validations/length.rb +26 -0
  231. data/lib/active_record/validations/presence.rb +68 -0
  232. data/lib/active_record/validations/uniqueness.rb +238 -0
  233. data/lib/active_record/version.rb +10 -0
  234. data/lib/rails/generators/active_record.rb +19 -0
  235. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  236. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  237. data/lib/rails/generators/active_record/migration.rb +35 -0
  238. data/lib/rails/generators/active_record/migration/migration_generator.rb +78 -0
  239. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  240. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +46 -0
  241. data/lib/rails/generators/active_record/model/model_generator.rb +48 -0
  242. data/lib/rails/generators/active_record/model/templates/model.rb.tt +13 -0
  243. data/lib/rails/generators/active_record/model/templates/module.rb.tt +7 -0
  244. metadata +333 -0
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ class SchemaCache
6
+ attr_reader :version
7
+ attr_accessor :connection
8
+
9
+ def initialize(conn)
10
+ @connection = conn
11
+
12
+ @columns = {}
13
+ @columns_hash = {}
14
+ @primary_keys = {}
15
+ @data_sources = {}
16
+ end
17
+
18
+ def initialize_dup(other)
19
+ super
20
+ @columns = @columns.dup
21
+ @columns_hash = @columns_hash.dup
22
+ @primary_keys = @primary_keys.dup
23
+ @data_sources = @data_sources.dup
24
+ end
25
+
26
+ def encode_with(coder)
27
+ coder["columns"] = @columns
28
+ coder["columns_hash"] = @columns_hash
29
+ coder["primary_keys"] = @primary_keys
30
+ coder["data_sources"] = @data_sources
31
+ coder["version"] = connection.migration_context.current_version
32
+ end
33
+
34
+ def init_with(coder)
35
+ @columns = coder["columns"]
36
+ @columns_hash = coder["columns_hash"]
37
+ @primary_keys = coder["primary_keys"]
38
+ @data_sources = coder["data_sources"]
39
+ @version = coder["version"]
40
+ end
41
+
42
+ def primary_keys(table_name)
43
+ @primary_keys[table_name] ||= data_source_exists?(table_name) ? connection.primary_key(table_name) : nil
44
+ end
45
+
46
+ # A cached lookup for table existence.
47
+ def data_source_exists?(name)
48
+ prepare_data_sources if @data_sources.empty?
49
+ return @data_sources[name] if @data_sources.key? name
50
+
51
+ @data_sources[name] = connection.data_source_exists?(name)
52
+ end
53
+
54
+ # Add internal cache for table with +table_name+.
55
+ def add(table_name)
56
+ if data_source_exists?(table_name)
57
+ primary_keys(table_name)
58
+ columns(table_name)
59
+ columns_hash(table_name)
60
+ end
61
+ end
62
+
63
+ def data_sources(name)
64
+ @data_sources[name]
65
+ end
66
+
67
+ # Get the columns for a table
68
+ def columns(table_name)
69
+ @columns[table_name] ||= connection.columns(table_name)
70
+ end
71
+
72
+ # Get the columns for a table as a hash, key is the column name
73
+ # value is the column object.
74
+ def columns_hash(table_name)
75
+ @columns_hash[table_name] ||= Hash[columns(table_name).map { |col|
76
+ [col.name, col]
77
+ }]
78
+ end
79
+
80
+ # Clears out internal caches
81
+ def clear!
82
+ @columns.clear
83
+ @columns_hash.clear
84
+ @primary_keys.clear
85
+ @data_sources.clear
86
+ @version = nil
87
+ end
88
+
89
+ def size
90
+ [@columns, @columns_hash, @primary_keys, @data_sources].map(&:size).inject :+
91
+ end
92
+
93
+ # Clear out internal caches for the data source +name+.
94
+ def clear_data_source_cache!(name)
95
+ @columns.delete name
96
+ @columns_hash.delete name
97
+ @primary_keys.delete name
98
+ @data_sources.delete name
99
+ end
100
+
101
+ def marshal_dump
102
+ # if we get current version during initialization, it happens stack over flow.
103
+ @version = connection.migration_context.current_version
104
+ [@version, @columns, @columns_hash, @primary_keys, @data_sources]
105
+ end
106
+
107
+ def marshal_load(array)
108
+ @version, @columns, @columns_hash, @primary_keys, @data_sources = array
109
+ end
110
+
111
+ private
112
+
113
+ def prepare_data_sources
114
+ connection.data_sources.each { |source| @data_sources[source] = true }
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ # :stopdoc:
5
+ module ConnectionAdapters
6
+ class SqlTypeMetadata
7
+ attr_reader :sql_type, :type, :limit, :precision, :scale
8
+
9
+ def initialize(sql_type: nil, type: nil, limit: nil, precision: nil, scale: nil)
10
+ @sql_type = sql_type
11
+ @type = type
12
+ @limit = limit
13
+ @precision = precision
14
+ @scale = scale
15
+ end
16
+
17
+ def ==(other)
18
+ other.is_a?(SqlTypeMetadata) &&
19
+ attributes_for_hash == other.attributes_for_hash
20
+ end
21
+ alias eql? ==
22
+
23
+ def hash
24
+ attributes_for_hash.hash
25
+ end
26
+
27
+ protected
28
+
29
+ def attributes_for_hash
30
+ [self.class, sql_type, type, limit, precision, scale]
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module SQLite3
6
+ class ExplainPrettyPrinter # :nodoc:
7
+ # Pretty prints the result of an EXPLAIN QUERY PLAN in a way that resembles
8
+ # the output of the SQLite shell:
9
+ #
10
+ # 0|0|0|SEARCH TABLE users USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
11
+ # 0|1|1|SCAN TABLE posts (~100000 rows)
12
+ #
13
+ def pp(result)
14
+ result.rows.map do |row|
15
+ row.join("|")
16
+ end.join("\n") + "\n"
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module SQLite3
6
+ module Quoting # :nodoc:
7
+ def quote_string(s)
8
+ @connection.class.quote(s)
9
+ end
10
+
11
+ def quote_table_name_for_assignment(table, attr)
12
+ quote_column_name(attr)
13
+ end
14
+
15
+ def quote_table_name(name)
16
+ @quoted_table_names[name] ||= super.gsub(".", "\".\"").freeze
17
+ end
18
+
19
+ def quote_column_name(name)
20
+ @quoted_column_names[name] ||= %Q("#{super.gsub('"', '""')}").freeze
21
+ end
22
+
23
+ def quoted_time(value)
24
+ value = value.change(year: 2000, month: 1, day: 1)
25
+ quoted_date(value).sub(/\A\d\d\d\d-\d\d-\d\d /, "2000-01-01 ")
26
+ end
27
+
28
+ def quoted_binary(value)
29
+ "x'#{value.hex}'"
30
+ end
31
+
32
+ def quoted_true
33
+ ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? "1".freeze : "'t'".freeze
34
+ end
35
+
36
+ def unquoted_true
37
+ ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? 1 : "t".freeze
38
+ end
39
+
40
+ def quoted_false
41
+ ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? "0".freeze : "'f'".freeze
42
+ end
43
+
44
+ def unquoted_false
45
+ ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? 0 : "f".freeze
46
+ end
47
+
48
+ private
49
+
50
+ def _type_cast(value)
51
+ case value
52
+ when BigDecimal
53
+ value.to_f
54
+ when String
55
+ if value.encoding == Encoding::ASCII_8BIT
56
+ super(value.encode(Encoding::UTF_8))
57
+ else
58
+ super
59
+ end
60
+ else
61
+ super
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module SQLite3
6
+ class SchemaCreation < AbstractAdapter::SchemaCreation # :nodoc:
7
+ private
8
+ def add_column_options!(sql, options)
9
+ if options[:collation]
10
+ sql << " COLLATE \"#{options[:collation]}\""
11
+ end
12
+ super
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module SQLite3
6
+ class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
7
+ def references(*args, **options)
8
+ super(*args, type: :integer, **options)
9
+ end
10
+ alias :belongs_to :references
11
+
12
+ private
13
+ def integer_like_primary_key_type(type, options)
14
+ :primary_key
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module SQLite3
6
+ class SchemaDumper < ConnectionAdapters::SchemaDumper # :nodoc:
7
+ private
8
+ def default_primary_key?(column)
9
+ schema_type(column) == :integer
10
+ end
11
+
12
+ def explicit_primary_key_default?(column)
13
+ column.bigint?
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module SQLite3
6
+ module SchemaStatements # :nodoc:
7
+ # Returns an array of indexes for the given table.
8
+ def indexes(table_name)
9
+ exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", "SCHEMA").map do |row|
10
+ # Indexes SQLite creates implicitly for internal use start with "sqlite_".
11
+ # See https://www.sqlite.org/fileformat2.html#intschema
12
+ next if row["name"].starts_with?("sqlite_")
13
+
14
+ index_sql = query_value(<<-SQL, "SCHEMA")
15
+ SELECT sql
16
+ FROM sqlite_master
17
+ WHERE name = #{quote(row['name'])} AND type = 'index'
18
+ UNION ALL
19
+ SELECT sql
20
+ FROM sqlite_temp_master
21
+ WHERE name = #{quote(row['name'])} AND type = 'index'
22
+ SQL
23
+
24
+ /\sWHERE\s+(?<where>.+)$/i =~ index_sql
25
+
26
+ columns = exec_query("PRAGMA index_info(#{quote(row['name'])})", "SCHEMA").map do |col|
27
+ col["name"]
28
+ end
29
+
30
+ # Add info on sort order for columns (only desc order is explicitly specified, asc is
31
+ # the default)
32
+ orders = {}
33
+ if index_sql # index_sql can be null in case of primary key indexes
34
+ index_sql.scan(/"(\w+)" DESC/).flatten.each { |order_column|
35
+ orders[order_column] = :desc
36
+ }
37
+ end
38
+
39
+ IndexDefinition.new(
40
+ table_name,
41
+ row["name"],
42
+ row["unique"] != 0,
43
+ columns,
44
+ where: where,
45
+ orders: orders
46
+ )
47
+ end.compact
48
+ end
49
+
50
+ def create_schema_dumper(options)
51
+ SQLite3::SchemaDumper.create(self, options)
52
+ end
53
+
54
+ private
55
+ def schema_creation
56
+ SQLite3::SchemaCreation.new(self)
57
+ end
58
+
59
+ def create_table_definition(*args)
60
+ SQLite3::TableDefinition.new(*args)
61
+ end
62
+
63
+ def new_column_from_field(table_name, field)
64
+ default = \
65
+ case field["dflt_value"]
66
+ when /^null$/i
67
+ nil
68
+ when /^'(.*)'$/m
69
+ $1.gsub("''", "'")
70
+ when /^"(.*)"$/m
71
+ $1.gsub('""', '"')
72
+ else
73
+ field["dflt_value"]
74
+ end
75
+
76
+ type_metadata = fetch_type_metadata(field["type"])
77
+ Column.new(field["name"], default, type_metadata, field["notnull"].to_i == 0, table_name, nil, field["collation"])
78
+ end
79
+
80
+ def data_source_sql(name = nil, type: nil)
81
+ scope = quoted_scope(name, type: type)
82
+ scope[:type] ||= "'table','view'"
83
+
84
+ sql = "SELECT name FROM sqlite_master WHERE name <> 'sqlite_sequence'".dup
85
+ sql << " AND name = #{scope[:name]}" if scope[:name]
86
+ sql << " AND type IN (#{scope[:type]})"
87
+ sql
88
+ end
89
+
90
+ def quoted_scope(name = nil, type: nil)
91
+ type = \
92
+ case type
93
+ when "BASE TABLE"
94
+ "'table'"
95
+ when "VIEW"
96
+ "'view'"
97
+ end
98
+ scope = {}
99
+ scope[:name] = quote(name) if name
100
+ scope[:type] = type if type
101
+ scope
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,573 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record/connection_adapters/abstract_adapter"
4
+ require "active_record/connection_adapters/statement_pool"
5
+ require "active_record/connection_adapters/sqlite3/explain_pretty_printer"
6
+ require "active_record/connection_adapters/sqlite3/quoting"
7
+ require "active_record/connection_adapters/sqlite3/schema_creation"
8
+ require "active_record/connection_adapters/sqlite3/schema_definitions"
9
+ require "active_record/connection_adapters/sqlite3/schema_dumper"
10
+ require "active_record/connection_adapters/sqlite3/schema_statements"
11
+
12
+ gem "sqlite3", "~> 1.3", ">= 1.3.6"
13
+ require "sqlite3"
14
+
15
+ module ActiveRecord
16
+ module ConnectionHandling # :nodoc:
17
+ def sqlite3_connection(config)
18
+ # Require database.
19
+ unless config[:database]
20
+ raise ArgumentError, "No database file specified. Missing argument: database"
21
+ end
22
+
23
+ # Allow database path relative to Rails.root, but only if the database
24
+ # path is not the special path that tells sqlite to build a database only
25
+ # in memory.
26
+ if ":memory:" != config[:database]
27
+ config[:database] = File.expand_path(config[:database], Rails.root) if defined?(Rails.root)
28
+ dirname = File.dirname(config[:database])
29
+ Dir.mkdir(dirname) unless File.directory?(dirname)
30
+ end
31
+
32
+ db = SQLite3::Database.new(
33
+ config[:database].to_s,
34
+ results_as_hash: true
35
+ )
36
+
37
+ db.busy_timeout(ConnectionAdapters::SQLite3Adapter.type_cast_config_to_integer(config[:timeout])) if config[:timeout]
38
+
39
+ ConnectionAdapters::SQLite3Adapter.new(db, logger, nil, config)
40
+ rescue Errno::ENOENT => error
41
+ if error.message.include?("No such file or directory")
42
+ raise ActiveRecord::NoDatabaseError
43
+ else
44
+ raise
45
+ end
46
+ end
47
+ end
48
+
49
+ module ConnectionAdapters #:nodoc:
50
+ # The SQLite3 adapter works SQLite 3.6.16 or newer
51
+ # with the sqlite3-ruby drivers (available as gem from https://rubygems.org/gems/sqlite3).
52
+ #
53
+ # Options:
54
+ #
55
+ # * <tt>:database</tt> - Path to the database file.
56
+ class SQLite3Adapter < AbstractAdapter
57
+ ADAPTER_NAME = "SQLite".freeze
58
+
59
+ include SQLite3::Quoting
60
+ include SQLite3::SchemaStatements
61
+
62
+ NATIVE_DATABASE_TYPES = {
63
+ primary_key: "integer PRIMARY KEY AUTOINCREMENT NOT NULL",
64
+ string: { name: "varchar" },
65
+ text: { name: "text" },
66
+ integer: { name: "integer" },
67
+ float: { name: "float" },
68
+ decimal: { name: "decimal" },
69
+ datetime: { name: "datetime" },
70
+ time: { name: "time" },
71
+ date: { name: "date" },
72
+ binary: { name: "blob" },
73
+ boolean: { name: "boolean" },
74
+ json: { name: "json" },
75
+ }
76
+
77
+ ##
78
+ # :singleton-method:
79
+ # Indicates whether boolean values are stored in sqlite3 databases as 1
80
+ # and 0 or 't' and 'f'. Leaving <tt>ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer</tt>
81
+ # set to false is deprecated. SQLite databases have used 't' and 'f' to
82
+ # serialize boolean values and must have old data converted to 1 and 0
83
+ # (its native boolean serialization) before setting this flag to true.
84
+ # Conversion can be accomplished by setting up a rake task which runs
85
+ #
86
+ # ExampleModel.where("boolean_column = 't'").update_all(boolean_column: 1)
87
+ # ExampleModel.where("boolean_column = 'f'").update_all(boolean_column: 0)
88
+ # for all models and all boolean columns, after which the flag must be set
89
+ # to true by adding the following to your <tt>application.rb</tt> file:
90
+ #
91
+ # Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true
92
+ class_attribute :represent_boolean_as_integer, default: false
93
+
94
+ class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
95
+ private
96
+ def dealloc(stmt)
97
+ stmt[:stmt].close unless stmt[:stmt].closed?
98
+ end
99
+ end
100
+
101
+ def initialize(connection, logger, connection_options, config)
102
+ super(connection, logger, config)
103
+
104
+ @active = true
105
+ @statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
106
+
107
+ configure_connection
108
+ end
109
+
110
+ def supports_ddl_transactions?
111
+ true
112
+ end
113
+
114
+ def supports_savepoints?
115
+ true
116
+ end
117
+
118
+ def supports_partial_index?
119
+ sqlite_version >= "3.8.0"
120
+ end
121
+
122
+ def requires_reloading?
123
+ true
124
+ end
125
+
126
+ def supports_foreign_keys_in_create?
127
+ sqlite_version >= "3.6.19"
128
+ end
129
+
130
+ def supports_views?
131
+ true
132
+ end
133
+
134
+ def supports_datetime_with_precision?
135
+ true
136
+ end
137
+
138
+ def supports_json?
139
+ true
140
+ end
141
+
142
+ def supports_multi_insert?
143
+ sqlite_version >= "3.7.11"
144
+ end
145
+
146
+ def active?
147
+ @active
148
+ end
149
+
150
+ # Disconnects from the database if already connected. Otherwise, this
151
+ # method does nothing.
152
+ def disconnect!
153
+ super
154
+ @active = false
155
+ @connection.close rescue nil
156
+ end
157
+
158
+ # Clears the prepared statements cache.
159
+ def clear_cache!
160
+ @statements.clear
161
+ end
162
+
163
+ def supports_index_sort_order?
164
+ true
165
+ end
166
+
167
+ # Returns 62. SQLite supports index names up to 64
168
+ # characters. The rest is used by Rails internally to perform
169
+ # temporary rename operations
170
+ def allowed_index_name_length
171
+ index_name_length - 2
172
+ end
173
+
174
+ def native_database_types #:nodoc:
175
+ NATIVE_DATABASE_TYPES
176
+ end
177
+
178
+ # Returns the current database encoding format as a string, eg: 'UTF-8'
179
+ def encoding
180
+ @connection.encoding.to_s
181
+ end
182
+
183
+ def supports_explain?
184
+ true
185
+ end
186
+
187
+ # REFERENTIAL INTEGRITY ====================================
188
+
189
+ def disable_referential_integrity # :nodoc:
190
+ old = query_value("PRAGMA foreign_keys")
191
+
192
+ begin
193
+ execute("PRAGMA foreign_keys = OFF")
194
+ yield
195
+ ensure
196
+ execute("PRAGMA foreign_keys = #{old}")
197
+ end
198
+ end
199
+
200
+ #--
201
+ # DATABASE STATEMENTS ======================================
202
+ #++
203
+
204
+ def explain(arel, binds = [])
205
+ sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
206
+ SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
207
+ end
208
+
209
+ def exec_query(sql, name = nil, binds = [], prepare: false)
210
+ type_casted_binds = type_casted_binds(binds)
211
+
212
+ log(sql, name, binds, type_casted_binds) do
213
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
214
+ # Don't cache statements if they are not prepared
215
+ unless prepare
216
+ stmt = @connection.prepare(sql)
217
+ begin
218
+ cols = stmt.columns
219
+ unless without_prepared_statement?(binds)
220
+ stmt.bind_params(type_casted_binds)
221
+ end
222
+ records = stmt.to_a
223
+ ensure
224
+ stmt.close
225
+ end
226
+ else
227
+ cache = @statements[sql] ||= {
228
+ stmt: @connection.prepare(sql)
229
+ }
230
+ stmt = cache[:stmt]
231
+ cols = cache[:cols] ||= stmt.columns
232
+ stmt.reset!
233
+ stmt.bind_params(type_casted_binds)
234
+ records = stmt.to_a
235
+ end
236
+
237
+ ActiveRecord::Result.new(cols, records)
238
+ end
239
+ end
240
+ end
241
+
242
+ def exec_delete(sql, name = "SQL", binds = [])
243
+ exec_query(sql, name, binds)
244
+ @connection.changes
245
+ end
246
+ alias :exec_update :exec_delete
247
+
248
+ def last_inserted_id(result)
249
+ @connection.last_insert_row_id
250
+ end
251
+
252
+ def execute(sql, name = nil) #:nodoc:
253
+ log(sql, name) do
254
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
255
+ @connection.execute(sql)
256
+ end
257
+ end
258
+ end
259
+
260
+ def begin_db_transaction #:nodoc:
261
+ log("begin transaction", nil) { @connection.transaction }
262
+ end
263
+
264
+ def commit_db_transaction #:nodoc:
265
+ log("commit transaction", nil) { @connection.commit }
266
+ end
267
+
268
+ def exec_rollback_db_transaction #:nodoc:
269
+ log("rollback transaction", nil) { @connection.rollback }
270
+ end
271
+
272
+ # SCHEMA STATEMENTS ========================================
273
+
274
+ def primary_keys(table_name) # :nodoc:
275
+ pks = table_structure(table_name).select { |f| f["pk"] > 0 }
276
+ pks.sort_by { |f| f["pk"] }.map { |f| f["name"] }
277
+ end
278
+
279
+ def remove_index(table_name, options = {}) #:nodoc:
280
+ index_name = index_name_for_remove(table_name, options)
281
+ exec_query "DROP INDEX #{quote_column_name(index_name)}"
282
+ end
283
+
284
+ # Renames a table.
285
+ #
286
+ # Example:
287
+ # rename_table('octopuses', 'octopi')
288
+ def rename_table(table_name, new_name)
289
+ exec_query "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
290
+ rename_table_indexes(table_name, new_name)
291
+ end
292
+
293
+ def valid_alter_table_type?(type, options = {})
294
+ !invalid_alter_table_type?(type, options)
295
+ end
296
+ deprecate :valid_alter_table_type?
297
+
298
+ def add_column(table_name, column_name, type, options = {}) #:nodoc:
299
+ if invalid_alter_table_type?(type, options)
300
+ alter_table(table_name) do |definition|
301
+ definition.column(column_name, type, options)
302
+ end
303
+ else
304
+ super
305
+ end
306
+ end
307
+
308
+ def remove_column(table_name, column_name, type = nil, options = {}) #:nodoc:
309
+ alter_table(table_name) do |definition|
310
+ definition.remove_column column_name
311
+ end
312
+ end
313
+
314
+ def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
315
+ default = extract_new_default_value(default_or_changes)
316
+
317
+ alter_table(table_name) do |definition|
318
+ definition[column_name].default = default
319
+ end
320
+ end
321
+
322
+ def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
323
+ unless null || default.nil?
324
+ exec_query("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
325
+ end
326
+ alter_table(table_name) do |definition|
327
+ definition[column_name].null = null
328
+ end
329
+ end
330
+
331
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
332
+ alter_table(table_name) do |definition|
333
+ definition[column_name].instance_eval do
334
+ self.type = type
335
+ self.limit = options[:limit] if options.include?(:limit)
336
+ self.default = options[:default] if options.include?(:default)
337
+ self.null = options[:null] if options.include?(:null)
338
+ self.precision = options[:precision] if options.include?(:precision)
339
+ self.scale = options[:scale] if options.include?(:scale)
340
+ self.collation = options[:collation] if options.include?(:collation)
341
+ end
342
+ end
343
+ end
344
+
345
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
346
+ column = column_for(table_name, column_name)
347
+ alter_table(table_name, rename: { column.name => new_column_name.to_s })
348
+ rename_column_indexes(table_name, column.name, new_column_name)
349
+ end
350
+
351
+ def add_reference(table_name, ref_name, **options) # :nodoc:
352
+ super(table_name, ref_name, type: :integer, **options)
353
+ end
354
+ alias :add_belongs_to :add_reference
355
+
356
+ def foreign_keys(table_name)
357
+ fk_info = exec_query("PRAGMA foreign_key_list(#{quote(table_name)})", "SCHEMA")
358
+ fk_info.map do |row|
359
+ options = {
360
+ column: row["from"],
361
+ primary_key: row["to"],
362
+ on_delete: extract_foreign_key_action(row["on_delete"]),
363
+ on_update: extract_foreign_key_action(row["on_update"])
364
+ }
365
+ ForeignKeyDefinition.new(table_name, row["table"], options)
366
+ end
367
+ end
368
+
369
+ def insert_fixtures(rows, table_name)
370
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
371
+ `insert_fixtures` is deprecated and will be removed in the next version of Rails.
372
+ Consider using `insert_fixtures_set` for performance improvement.
373
+ MSG
374
+ insert_fixtures_set(table_name => rows)
375
+ end
376
+
377
+ def insert_fixtures_set(fixture_set, tables_to_delete = [])
378
+ disable_referential_integrity do
379
+ transaction(requires_new: true) do
380
+ tables_to_delete.each { |table| delete "DELETE FROM #{quote_table_name(table)}", "Fixture Delete" }
381
+
382
+ fixture_set.each do |table_name, rows|
383
+ rows.each { |row| insert_fixture(row, table_name) }
384
+ end
385
+ end
386
+ end
387
+ end
388
+
389
+ private
390
+ def initialize_type_map(m = type_map)
391
+ super
392
+ register_class_with_limit m, %r(int)i, SQLite3Integer
393
+ end
394
+
395
+ def table_structure(table_name)
396
+ structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
397
+ raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
398
+ table_structure_with_collation(table_name, structure)
399
+ end
400
+ alias column_definitions table_structure
401
+
402
+ # See: https://www.sqlite.org/lang_altertable.html
403
+ # SQLite has an additional restriction on the ALTER TABLE statement
404
+ def invalid_alter_table_type?(type, options)
405
+ type.to_sym == :primary_key || options[:primary_key]
406
+ end
407
+
408
+ def alter_table(table_name, options = {})
409
+ altered_table_name = "a#{table_name}"
410
+ caller = lambda { |definition| yield definition if block_given? }
411
+
412
+ transaction do
413
+ move_table(table_name, altered_table_name,
414
+ options.merge(temporary: true))
415
+ move_table(altered_table_name, table_name, &caller)
416
+ end
417
+ end
418
+
419
+ def move_table(from, to, options = {}, &block)
420
+ copy_table(from, to, options, &block)
421
+ drop_table(from)
422
+ end
423
+
424
+ def copy_table(from, to, options = {})
425
+ from_primary_key = primary_key(from)
426
+ options[:id] = false
427
+ create_table(to, options) do |definition|
428
+ @definition = definition
429
+ if from_primary_key.is_a?(Array)
430
+ @definition.primary_keys from_primary_key
431
+ end
432
+ columns(from).each do |column|
433
+ column_name = options[:rename] ?
434
+ (options[:rename][column.name] ||
435
+ options[:rename][column.name.to_sym] ||
436
+ column.name) : column.name
437
+
438
+ @definition.column(column_name, column.type,
439
+ limit: column.limit, default: column.default,
440
+ precision: column.precision, scale: column.scale,
441
+ null: column.null, collation: column.collation,
442
+ primary_key: column_name == from_primary_key
443
+ )
444
+ end
445
+ yield @definition if block_given?
446
+ end
447
+ copy_table_indexes(from, to, options[:rename] || {})
448
+ copy_table_contents(from, to,
449
+ @definition.columns.map(&:name),
450
+ options[:rename] || {})
451
+ end
452
+
453
+ def copy_table_indexes(from, to, rename = {})
454
+ indexes(from).each do |index|
455
+ name = index.name
456
+ if to == "a#{from}"
457
+ name = "t#{name}"
458
+ elsif from == "a#{to}"
459
+ name = name[1..-1]
460
+ end
461
+
462
+ to_column_names = columns(to).map(&:name)
463
+ columns = index.columns.map { |c| rename[c] || c }.select do |column|
464
+ to_column_names.include?(column)
465
+ end
466
+
467
+ unless columns.empty?
468
+ # index name can't be the same
469
+ opts = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
470
+ opts[:unique] = true if index.unique
471
+ opts[:where] = index.where if index.where
472
+ add_index(to, columns, opts)
473
+ end
474
+ end
475
+ end
476
+
477
+ def copy_table_contents(from, to, columns, rename = {})
478
+ column_mappings = Hash[columns.map { |name| [name, name] }]
479
+ rename.each { |a| column_mappings[a.last] = a.first }
480
+ from_columns = columns(from).collect(&:name)
481
+ columns = columns.find_all { |col| from_columns.include?(column_mappings[col]) }
482
+ from_columns_to_copy = columns.map { |col| column_mappings[col] }
483
+ quoted_columns = columns.map { |col| quote_column_name(col) } * ","
484
+ quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ","
485
+
486
+ exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
487
+ SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
488
+ end
489
+
490
+ def sqlite_version
491
+ @sqlite_version ||= SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
492
+ end
493
+
494
+ def translate_exception(exception, message)
495
+ case exception.message
496
+ # SQLite 3.8.2 returns a newly formatted error message:
497
+ # UNIQUE constraint failed: *table_name*.*column_name*
498
+ # Older versions of SQLite return:
499
+ # column *column_name* is not unique
500
+ when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
501
+ RecordNotUnique.new(message)
502
+ when /.* may not be NULL/, /NOT NULL constraint failed: .*/
503
+ NotNullViolation.new(message)
504
+ when /FOREIGN KEY constraint failed/i
505
+ InvalidForeignKey.new(message)
506
+ else
507
+ super
508
+ end
509
+ end
510
+
511
+ COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze
512
+
513
+ def table_structure_with_collation(table_name, basic_structure)
514
+ collation_hash = {}
515
+ sql = <<-SQL
516
+ SELECT sql FROM
517
+ (SELECT * FROM sqlite_master UNION ALL
518
+ SELECT * FROM sqlite_temp_master)
519
+ WHERE type = 'table' AND name = #{quote(table_name)}
520
+ SQL
521
+
522
+ # Result will have following sample string
523
+ # CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
524
+ # "password_digest" varchar COLLATE "NOCASE");
525
+ result = exec_query(sql, "SCHEMA").first
526
+
527
+ if result
528
+ # Splitting with left parentheses and picking up last will return all
529
+ # columns separated with comma(,).
530
+ columns_string = result["sql"].split("(").last
531
+
532
+ columns_string.split(",").each do |column_string|
533
+ # This regex will match the column name and collation type and will save
534
+ # the value in $1 and $2 respectively.
535
+ collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
536
+ end
537
+
538
+ basic_structure.map! do |column|
539
+ column_name = column["name"]
540
+
541
+ if collation_hash.has_key? column_name
542
+ column["collation"] = collation_hash[column_name]
543
+ end
544
+
545
+ column
546
+ end
547
+ else
548
+ basic_structure.to_hash
549
+ end
550
+ end
551
+
552
+ def arel_visitor
553
+ Arel::Visitors::SQLite.new(self)
554
+ end
555
+
556
+ def configure_connection
557
+ execute("PRAGMA foreign_keys = ON", "SCHEMA")
558
+ end
559
+
560
+ class SQLite3Integer < Type::Integer # :nodoc:
561
+ private
562
+ def _limit
563
+ # INTEGER storage class can be stored 8 bytes value.
564
+ # See https://www.sqlite.org/datatype3.html#storage_classes_and_datatypes
565
+ limit || 8
566
+ end
567
+ end
568
+
569
+ ActiveRecord::Type.register(:integer, SQLite3Integer, adapter: :sqlite3)
570
+ end
571
+ ActiveSupport.run_load_hooks(:active_record_sqlite3adapter, SQLite3Adapter)
572
+ end
573
+ end