activerecord 4.2.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 (221) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1372 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +218 -0
  5. data/examples/performance.rb +184 -0
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record.rb +173 -0
  8. data/lib/active_record/aggregations.rb +266 -0
  9. data/lib/active_record/association_relation.rb +22 -0
  10. data/lib/active_record/associations.rb +1724 -0
  11. data/lib/active_record/associations/alias_tracker.rb +87 -0
  12. data/lib/active_record/associations/association.rb +253 -0
  13. data/lib/active_record/associations/association_scope.rb +194 -0
  14. data/lib/active_record/associations/belongs_to_association.rb +111 -0
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +40 -0
  16. data/lib/active_record/associations/builder/association.rb +149 -0
  17. data/lib/active_record/associations/builder/belongs_to.rb +116 -0
  18. data/lib/active_record/associations/builder/collection_association.rb +91 -0
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +124 -0
  20. data/lib/active_record/associations/builder/has_many.rb +15 -0
  21. data/lib/active_record/associations/builder/has_one.rb +23 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +38 -0
  23. data/lib/active_record/associations/collection_association.rb +634 -0
  24. data/lib/active_record/associations/collection_proxy.rb +1027 -0
  25. data/lib/active_record/associations/has_many_association.rb +184 -0
  26. data/lib/active_record/associations/has_many_through_association.rb +238 -0
  27. data/lib/active_record/associations/has_one_association.rb +105 -0
  28. data/lib/active_record/associations/has_one_through_association.rb +36 -0
  29. data/lib/active_record/associations/join_dependency.rb +282 -0
  30. data/lib/active_record/associations/join_dependency/join_association.rb +122 -0
  31. data/lib/active_record/associations/join_dependency/join_base.rb +22 -0
  32. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  33. data/lib/active_record/associations/preloader.rb +203 -0
  34. data/lib/active_record/associations/preloader/association.rb +162 -0
  35. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  36. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  37. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  38. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  39. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  40. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  41. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  42. data/lib/active_record/associations/preloader/through_association.rb +96 -0
  43. data/lib/active_record/associations/singular_association.rb +86 -0
  44. data/lib/active_record/associations/through_association.rb +96 -0
  45. data/lib/active_record/attribute.rb +149 -0
  46. data/lib/active_record/attribute_assignment.rb +212 -0
  47. data/lib/active_record/attribute_decorators.rb +66 -0
  48. data/lib/active_record/attribute_methods.rb +439 -0
  49. data/lib/active_record/attribute_methods/before_type_cast.rb +71 -0
  50. data/lib/active_record/attribute_methods/dirty.rb +181 -0
  51. data/lib/active_record/attribute_methods/primary_key.rb +128 -0
  52. data/lib/active_record/attribute_methods/query.rb +40 -0
  53. data/lib/active_record/attribute_methods/read.rb +103 -0
  54. data/lib/active_record/attribute_methods/serialization.rb +70 -0
  55. data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -0
  56. data/lib/active_record/attribute_methods/write.rb +83 -0
  57. data/lib/active_record/attribute_set.rb +77 -0
  58. data/lib/active_record/attribute_set/builder.rb +86 -0
  59. data/lib/active_record/attributes.rb +139 -0
  60. data/lib/active_record/autosave_association.rb +439 -0
  61. data/lib/active_record/base.rb +317 -0
  62. data/lib/active_record/callbacks.rb +313 -0
  63. data/lib/active_record/coders/json.rb +13 -0
  64. data/lib/active_record/coders/yaml_column.rb +38 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +659 -0
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +373 -0
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +133 -0
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +574 -0
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +991 -0
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +219 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +487 -0
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +883 -0
  78. data/lib/active_record/connection_adapters/column.rb +82 -0
  79. data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
  80. data/lib/active_record/connection_adapters/mysql2_adapter.rb +282 -0
  81. data/lib/active_record/connection_adapters/mysql_adapter.rb +491 -0
  82. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
  83. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  84. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  111. data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
  112. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  114. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +588 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +754 -0
  117. data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +628 -0
  119. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  120. data/lib/active_record/connection_handling.rb +132 -0
  121. data/lib/active_record/core.rb +566 -0
  122. data/lib/active_record/counter_cache.rb +175 -0
  123. data/lib/active_record/dynamic_matchers.rb +140 -0
  124. data/lib/active_record/enum.rb +198 -0
  125. data/lib/active_record/errors.rb +252 -0
  126. data/lib/active_record/explain.rb +38 -0
  127. data/lib/active_record/explain_registry.rb +30 -0
  128. data/lib/active_record/explain_subscriber.rb +29 -0
  129. data/lib/active_record/fixture_set/file.rb +56 -0
  130. data/lib/active_record/fixtures.rb +1007 -0
  131. data/lib/active_record/gem_version.rb +15 -0
  132. data/lib/active_record/inheritance.rb +247 -0
  133. data/lib/active_record/integration.rb +113 -0
  134. data/lib/active_record/locale/en.yml +47 -0
  135. data/lib/active_record/locking/optimistic.rb +204 -0
  136. data/lib/active_record/locking/pessimistic.rb +77 -0
  137. data/lib/active_record/log_subscriber.rb +75 -0
  138. data/lib/active_record/migration.rb +1051 -0
  139. data/lib/active_record/migration/command_recorder.rb +197 -0
  140. data/lib/active_record/migration/join_table.rb +15 -0
  141. data/lib/active_record/model_schema.rb +340 -0
  142. data/lib/active_record/nested_attributes.rb +548 -0
  143. data/lib/active_record/no_touching.rb +52 -0
  144. data/lib/active_record/null_relation.rb +81 -0
  145. data/lib/active_record/persistence.rb +532 -0
  146. data/lib/active_record/query_cache.rb +56 -0
  147. data/lib/active_record/querying.rb +68 -0
  148. data/lib/active_record/railtie.rb +162 -0
  149. data/lib/active_record/railties/console_sandbox.rb +5 -0
  150. data/lib/active_record/railties/controller_runtime.rb +50 -0
  151. data/lib/active_record/railties/databases.rake +391 -0
  152. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  153. data/lib/active_record/readonly_attributes.rb +23 -0
  154. data/lib/active_record/reflection.rb +881 -0
  155. data/lib/active_record/relation.rb +681 -0
  156. data/lib/active_record/relation/batches.rb +138 -0
  157. data/lib/active_record/relation/calculations.rb +403 -0
  158. data/lib/active_record/relation/delegation.rb +140 -0
  159. data/lib/active_record/relation/finder_methods.rb +528 -0
  160. data/lib/active_record/relation/merger.rb +170 -0
  161. data/lib/active_record/relation/predicate_builder.rb +126 -0
  162. data/lib/active_record/relation/predicate_builder/array_handler.rb +47 -0
  163. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  164. data/lib/active_record/relation/query_methods.rb +1176 -0
  165. data/lib/active_record/relation/spawn_methods.rb +75 -0
  166. data/lib/active_record/result.rb +131 -0
  167. data/lib/active_record/runtime_registry.rb +22 -0
  168. data/lib/active_record/sanitization.rb +191 -0
  169. data/lib/active_record/schema.rb +64 -0
  170. data/lib/active_record/schema_dumper.rb +251 -0
  171. data/lib/active_record/schema_migration.rb +56 -0
  172. data/lib/active_record/scoping.rb +87 -0
  173. data/lib/active_record/scoping/default.rb +134 -0
  174. data/lib/active_record/scoping/named.rb +164 -0
  175. data/lib/active_record/serialization.rb +22 -0
  176. data/lib/active_record/serializers/xml_serializer.rb +193 -0
  177. data/lib/active_record/statement_cache.rb +111 -0
  178. data/lib/active_record/store.rb +205 -0
  179. data/lib/active_record/tasks/database_tasks.rb +296 -0
  180. data/lib/active_record/tasks/mysql_database_tasks.rb +145 -0
  181. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  182. data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
  183. data/lib/active_record/timestamp.rb +121 -0
  184. data/lib/active_record/transactions.rb +417 -0
  185. data/lib/active_record/translation.rb +22 -0
  186. data/lib/active_record/type.rb +23 -0
  187. data/lib/active_record/type/big_integer.rb +13 -0
  188. data/lib/active_record/type/binary.rb +50 -0
  189. data/lib/active_record/type/boolean.rb +30 -0
  190. data/lib/active_record/type/date.rb +46 -0
  191. data/lib/active_record/type/date_time.rb +43 -0
  192. data/lib/active_record/type/decimal.rb +40 -0
  193. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  194. data/lib/active_record/type/decorator.rb +14 -0
  195. data/lib/active_record/type/float.rb +19 -0
  196. data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
  197. data/lib/active_record/type/integer.rb +55 -0
  198. data/lib/active_record/type/mutable.rb +16 -0
  199. data/lib/active_record/type/numeric.rb +36 -0
  200. data/lib/active_record/type/serialized.rb +56 -0
  201. data/lib/active_record/type/string.rb +36 -0
  202. data/lib/active_record/type/text.rb +11 -0
  203. data/lib/active_record/type/time.rb +26 -0
  204. data/lib/active_record/type/time_value.rb +38 -0
  205. data/lib/active_record/type/type_map.rb +64 -0
  206. data/lib/active_record/type/unsigned_integer.rb +15 -0
  207. data/lib/active_record/type/value.rb +101 -0
  208. data/lib/active_record/validations.rb +90 -0
  209. data/lib/active_record/validations/associated.rb +51 -0
  210. data/lib/active_record/validations/presence.rb +67 -0
  211. data/lib/active_record/validations/uniqueness.rb +229 -0
  212. data/lib/active_record/version.rb +8 -0
  213. data/lib/rails/generators/active_record.rb +17 -0
  214. data/lib/rails/generators/active_record/migration.rb +18 -0
  215. data/lib/rails/generators/active_record/migration/migration_generator.rb +70 -0
  216. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +22 -0
  217. data/lib/rails/generators/active_record/migration/templates/migration.rb +45 -0
  218. data/lib/rails/generators/active_record/model/model_generator.rb +52 -0
  219. data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
  220. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  221. metadata +309 -0
@@ -0,0 +1,94 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ class SchemaCache
4
+ attr_reader :version
5
+ attr_accessor :connection
6
+
7
+ def initialize(conn)
8
+ @connection = conn
9
+
10
+ @columns = {}
11
+ @columns_hash = {}
12
+ @primary_keys = {}
13
+ @tables = {}
14
+ end
15
+
16
+ def primary_keys(table_name)
17
+ @primary_keys[table_name] ||= table_exists?(table_name) ? connection.primary_key(table_name) : nil
18
+ end
19
+
20
+ # A cached lookup for table existence.
21
+ def table_exists?(name)
22
+ prepare_tables if @tables.empty?
23
+ return @tables[name] if @tables.key? name
24
+
25
+ @tables[name] = connection.table_exists?(name)
26
+ end
27
+
28
+ # Add internal cache for table with +table_name+.
29
+ def add(table_name)
30
+ if table_exists?(table_name)
31
+ primary_keys(table_name)
32
+ columns(table_name)
33
+ columns_hash(table_name)
34
+ end
35
+ end
36
+
37
+ def tables(name)
38
+ @tables[name]
39
+ end
40
+
41
+ # Get the columns for a table
42
+ def columns(table_name)
43
+ @columns[table_name] ||= connection.columns(table_name)
44
+ end
45
+
46
+ # Get the columns for a table as a hash, key is the column name
47
+ # value is the column object.
48
+ def columns_hash(table_name)
49
+ @columns_hash[table_name] ||= Hash[columns(table_name).map { |col|
50
+ [col.name, col]
51
+ }]
52
+ end
53
+
54
+ # Clears out internal caches
55
+ def clear!
56
+ @columns.clear
57
+ @columns_hash.clear
58
+ @primary_keys.clear
59
+ @tables.clear
60
+ @version = nil
61
+ end
62
+
63
+ def size
64
+ [@columns, @columns_hash, @primary_keys, @tables].map { |x|
65
+ x.size
66
+ }.inject :+
67
+ end
68
+
69
+ # Clear out internal caches for table with +table_name+.
70
+ def clear_table_cache!(table_name)
71
+ @columns.delete table_name
72
+ @columns_hash.delete table_name
73
+ @primary_keys.delete table_name
74
+ @tables.delete table_name
75
+ end
76
+
77
+ def marshal_dump
78
+ # if we get current version during initialization, it happens stack over flow.
79
+ @version = ActiveRecord::Migrator.current_version
80
+ [@version, @columns, @columns_hash, @primary_keys, @tables]
81
+ end
82
+
83
+ def marshal_load(array)
84
+ @version, @columns, @columns_hash, @primary_keys, @tables = array
85
+ end
86
+
87
+ private
88
+
89
+ def prepare_tables
90
+ connection.tables.each { |table| @tables[table] = true }
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,628 @@
1
+ require 'active_record/connection_adapters/abstract_adapter'
2
+ require 'active_record/connection_adapters/statement_pool'
3
+ require 'arel/visitors/bind_visitor'
4
+
5
+ gem 'sqlite3', '~> 1.3.6'
6
+ require 'sqlite3'
7
+
8
+ module ActiveRecord
9
+ module ConnectionHandling # :nodoc:
10
+ # sqlite3 adapter reuses sqlite_connection.
11
+ def sqlite3_connection(config)
12
+ # Require database.
13
+ unless config[:database]
14
+ raise ArgumentError, "No database file specified. Missing argument: database"
15
+ end
16
+
17
+ # Allow database path relative to Rails.root, but only if the database
18
+ # path is not the special path that tells sqlite to build a database only
19
+ # in memory.
20
+ if ':memory:' != config[:database]
21
+ config[:database] = File.expand_path(config[:database], Rails.root) if defined?(Rails.root)
22
+ dirname = File.dirname(config[:database])
23
+ Dir.mkdir(dirname) unless File.directory?(dirname)
24
+ end
25
+
26
+ db = SQLite3::Database.new(
27
+ config[:database].to_s,
28
+ :results_as_hash => true
29
+ )
30
+
31
+ db.busy_timeout(ConnectionAdapters::SQLite3Adapter.type_cast_config_to_integer(config[:timeout])) if config[:timeout]
32
+
33
+ ConnectionAdapters::SQLite3Adapter.new(db, logger, nil, config)
34
+ rescue Errno::ENOENT => error
35
+ if error.message.include?("No such file or directory")
36
+ raise ActiveRecord::NoDatabaseError.new(error.message, error)
37
+ else
38
+ raise
39
+ end
40
+ end
41
+ end
42
+
43
+ module ConnectionAdapters #:nodoc:
44
+ class SQLite3Binary < Type::Binary # :nodoc:
45
+ def cast_value(value)
46
+ if value.encoding != Encoding::ASCII_8BIT
47
+ value = value.force_encoding(Encoding::ASCII_8BIT)
48
+ end
49
+ value
50
+ end
51
+ end
52
+
53
+ class SQLite3String < Type::String # :nodoc:
54
+ def type_cast_for_database(value)
55
+ if value.is_a?(::String) && value.encoding == Encoding::ASCII_8BIT
56
+ value.encode(Encoding::UTF_8)
57
+ else
58
+ super
59
+ end
60
+ end
61
+ end
62
+
63
+ # The SQLite3 adapter works SQLite 3.6.16 or newer
64
+ # with the sqlite3-ruby drivers (available as gem from https://rubygems.org/gems/sqlite3).
65
+ #
66
+ # Options:
67
+ #
68
+ # * <tt>:database</tt> - Path to the database file.
69
+ class SQLite3Adapter < AbstractAdapter
70
+ ADAPTER_NAME = 'SQLite'.freeze
71
+ include Savepoints
72
+
73
+ NATIVE_DATABASE_TYPES = {
74
+ primary_key: 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL',
75
+ string: { name: "varchar" },
76
+ text: { name: "text" },
77
+ integer: { name: "integer" },
78
+ float: { name: "float" },
79
+ decimal: { name: "decimal" },
80
+ datetime: { name: "datetime" },
81
+ time: { name: "time" },
82
+ date: { name: "date" },
83
+ binary: { name: "blob" },
84
+ boolean: { name: "boolean" }
85
+ }
86
+
87
+ class Version
88
+ include Comparable
89
+
90
+ def initialize(version_string)
91
+ @version = version_string.split('.').map { |v| v.to_i }
92
+ end
93
+
94
+ def <=>(version_string)
95
+ @version <=> version_string.split('.').map { |v| v.to_i }
96
+ end
97
+ end
98
+
99
+ class StatementPool < ConnectionAdapters::StatementPool
100
+ def initialize(connection, max)
101
+ super
102
+ @cache = Hash.new { |h,pid| h[pid] = {} }
103
+ end
104
+
105
+ def each(&block); cache.each(&block); end
106
+ def key?(key); cache.key?(key); end
107
+ def [](key); cache[key]; end
108
+ def length; cache.length; end
109
+
110
+ def []=(sql, key)
111
+ while @max <= cache.size
112
+ dealloc(cache.shift.last[:stmt])
113
+ end
114
+ cache[sql] = key
115
+ end
116
+
117
+ def clear
118
+ cache.each_value do |hash|
119
+ dealloc hash[:stmt]
120
+ end
121
+ cache.clear
122
+ end
123
+
124
+ private
125
+ def cache
126
+ @cache[$$]
127
+ end
128
+
129
+ def dealloc(stmt)
130
+ stmt.close unless stmt.closed?
131
+ end
132
+ end
133
+
134
+ def initialize(connection, logger, connection_options, config)
135
+ super(connection, logger)
136
+
137
+ @active = nil
138
+ @statements = StatementPool.new(@connection,
139
+ self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 }))
140
+ @config = config
141
+
142
+ @visitor = Arel::Visitors::SQLite.new self
143
+
144
+ if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
145
+ @prepared_statements = true
146
+ else
147
+ @prepared_statements = false
148
+ end
149
+ end
150
+
151
+ def supports_ddl_transactions?
152
+ true
153
+ end
154
+
155
+ def supports_savepoints?
156
+ true
157
+ end
158
+
159
+ def supports_partial_index?
160
+ sqlite_version >= '3.8.0'
161
+ end
162
+
163
+ # Returns true, since this connection adapter supports prepared statement
164
+ # caching.
165
+ def supports_statement_cache?
166
+ true
167
+ end
168
+
169
+ # Returns true, since this connection adapter supports migrations.
170
+ def supports_migrations? #:nodoc:
171
+ true
172
+ end
173
+
174
+ def supports_primary_key? #:nodoc:
175
+ true
176
+ end
177
+
178
+ def requires_reloading?
179
+ true
180
+ end
181
+
182
+ def supports_views?
183
+ true
184
+ end
185
+
186
+ def active?
187
+ @active != false
188
+ end
189
+
190
+ # Disconnects from the database if already connected. Otherwise, this
191
+ # method does nothing.
192
+ def disconnect!
193
+ super
194
+ @active = false
195
+ @connection.close rescue nil
196
+ end
197
+
198
+ # Clears the prepared statements cache.
199
+ def clear_cache!
200
+ @statements.clear
201
+ end
202
+
203
+ def supports_index_sort_order?
204
+ true
205
+ end
206
+
207
+ # Returns 62. SQLite supports index names up to 64
208
+ # characters. The rest is used by rails internally to perform
209
+ # temporary rename operations
210
+ def allowed_index_name_length
211
+ index_name_length - 2
212
+ end
213
+
214
+ def native_database_types #:nodoc:
215
+ NATIVE_DATABASE_TYPES
216
+ end
217
+
218
+ # Returns the current database encoding format as a string, eg: 'UTF-8'
219
+ def encoding
220
+ @connection.encoding.to_s
221
+ end
222
+
223
+ def supports_explain?
224
+ true
225
+ end
226
+
227
+ # QUOTING ==================================================
228
+
229
+ def _quote(value) # :nodoc:
230
+ case value
231
+ when Type::Binary::Data
232
+ "x'#{value.hex}'"
233
+ else
234
+ super
235
+ end
236
+ end
237
+
238
+ def _type_cast(value) # :nodoc:
239
+ case value
240
+ when BigDecimal
241
+ value.to_f
242
+ else
243
+ super
244
+ end
245
+ end
246
+
247
+ def quote_string(s) #:nodoc:
248
+ @connection.class.quote(s)
249
+ end
250
+
251
+ def quote_table_name_for_assignment(table, attr)
252
+ quote_column_name(attr)
253
+ end
254
+
255
+ def quote_column_name(name) #:nodoc:
256
+ %Q("#{name.to_s.gsub('"', '""')}")
257
+ end
258
+
259
+ # Quote date/time values for use in SQL input. Includes microseconds
260
+ # if the value is a Time responding to usec.
261
+ def quoted_date(value) #:nodoc:
262
+ if value.respond_to?(:usec)
263
+ "#{super}.#{sprintf("%06d", value.usec)}"
264
+ else
265
+ super
266
+ end
267
+ end
268
+
269
+ #--
270
+ # DATABASE STATEMENTS ======================================
271
+ #++
272
+
273
+ def explain(arel, binds = [])
274
+ sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
275
+ ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', []))
276
+ end
277
+
278
+ class ExplainPrettyPrinter
279
+ # Pretty prints the result of a EXPLAIN QUERY PLAN in a way that resembles
280
+ # the output of the SQLite shell:
281
+ #
282
+ # 0|0|0|SEARCH TABLE users USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
283
+ # 0|1|1|SCAN TABLE posts (~100000 rows)
284
+ #
285
+ def pp(result) # :nodoc:
286
+ result.rows.map do |row|
287
+ row.join('|')
288
+ end.join("\n") + "\n"
289
+ end
290
+ end
291
+
292
+ def exec_query(sql, name = nil, binds = [])
293
+ type_casted_binds = binds.map { |col, val|
294
+ [col, type_cast(val, col)]
295
+ }
296
+
297
+ log(sql, name, type_casted_binds) do
298
+ # Don't cache statements if they are not prepared
299
+ if without_prepared_statement?(binds)
300
+ stmt = @connection.prepare(sql)
301
+ begin
302
+ cols = stmt.columns
303
+ records = stmt.to_a
304
+ ensure
305
+ stmt.close
306
+ end
307
+ stmt = records
308
+ else
309
+ cache = @statements[sql] ||= {
310
+ :stmt => @connection.prepare(sql)
311
+ }
312
+ stmt = cache[:stmt]
313
+ cols = cache[:cols] ||= stmt.columns
314
+ stmt.reset!
315
+ stmt.bind_params type_casted_binds.map { |_, val| val }
316
+ end
317
+
318
+ ActiveRecord::Result.new(cols, stmt.to_a)
319
+ end
320
+ end
321
+
322
+ def exec_delete(sql, name = 'SQL', binds = [])
323
+ exec_query(sql, name, binds)
324
+ @connection.changes
325
+ end
326
+ alias :exec_update :exec_delete
327
+
328
+ def last_inserted_id(result)
329
+ @connection.last_insert_row_id
330
+ end
331
+
332
+ def execute(sql, name = nil) #:nodoc:
333
+ log(sql, name) { @connection.execute(sql) }
334
+ end
335
+
336
+ def update_sql(sql, name = nil) #:nodoc:
337
+ super
338
+ @connection.changes
339
+ end
340
+
341
+ def delete_sql(sql, name = nil) #:nodoc:
342
+ sql += " WHERE 1=1" unless sql =~ /WHERE/i
343
+ super sql, name
344
+ end
345
+
346
+ def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
347
+ super
348
+ id_value || @connection.last_insert_row_id
349
+ end
350
+ alias :create :insert_sql
351
+
352
+ def select_rows(sql, name = nil, binds = [])
353
+ exec_query(sql, name, binds).rows
354
+ end
355
+
356
+ def begin_db_transaction #:nodoc:
357
+ log('begin transaction',nil) { @connection.transaction }
358
+ end
359
+
360
+ def commit_db_transaction #:nodoc:
361
+ log('commit transaction',nil) { @connection.commit }
362
+ end
363
+
364
+ def rollback_db_transaction #:nodoc:
365
+ log('rollback transaction',nil) { @connection.rollback }
366
+ end
367
+
368
+ # SCHEMA STATEMENTS ========================================
369
+
370
+ def tables(name = nil, table_name = nil) #:nodoc:
371
+ sql = <<-SQL
372
+ SELECT name
373
+ FROM sqlite_master
374
+ WHERE (type = 'table' OR type = 'view') AND NOT name = 'sqlite_sequence'
375
+ SQL
376
+ sql << " AND name = #{quote_table_name(table_name)}" if table_name
377
+
378
+ exec_query(sql, 'SCHEMA').map do |row|
379
+ row['name']
380
+ end
381
+ end
382
+
383
+ def table_exists?(table_name)
384
+ table_name && tables(nil, table_name).any?
385
+ end
386
+
387
+ # Returns an array of +Column+ objects for the table specified by +table_name+.
388
+ def columns(table_name) #:nodoc:
389
+ table_structure(table_name).map do |field|
390
+ case field["dflt_value"]
391
+ when /^null$/i
392
+ field["dflt_value"] = nil
393
+ when /^'(.*)'$/m
394
+ field["dflt_value"] = $1.gsub("''", "'")
395
+ when /^"(.*)"$/m
396
+ field["dflt_value"] = $1.gsub('""', '"')
397
+ end
398
+
399
+ sql_type = field['type']
400
+ cast_type = lookup_cast_type(sql_type)
401
+ new_column(field['name'], field['dflt_value'], cast_type, sql_type, field['notnull'].to_i == 0)
402
+ end
403
+ end
404
+
405
+ # Returns an array of indexes for the given table.
406
+ def indexes(table_name, name = nil) #:nodoc:
407
+ exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", 'SCHEMA').map do |row|
408
+ sql = <<-SQL
409
+ SELECT sql
410
+ FROM sqlite_master
411
+ WHERE name=#{quote(row['name'])} AND type='index'
412
+ UNION ALL
413
+ SELECT sql
414
+ FROM sqlite_temp_master
415
+ WHERE name=#{quote(row['name'])} AND type='index'
416
+ SQL
417
+ index_sql = exec_query(sql).first['sql']
418
+ match = /\sWHERE\s+(.+)$/i.match(index_sql)
419
+ where = match[1] if match
420
+ IndexDefinition.new(
421
+ table_name,
422
+ row['name'],
423
+ row['unique'] != 0,
424
+ exec_query("PRAGMA index_info('#{row['name']}')", "SCHEMA").map { |col|
425
+ col['name']
426
+ }, nil, nil, where)
427
+ end
428
+ end
429
+
430
+ def primary_key(table_name) #:nodoc:
431
+ column = table_structure(table_name).find { |field|
432
+ field['pk'] == 1
433
+ }
434
+ column && column['name']
435
+ end
436
+
437
+ def remove_index!(table_name, index_name) #:nodoc:
438
+ exec_query "DROP INDEX #{quote_column_name(index_name)}"
439
+ end
440
+
441
+ # Renames a table.
442
+ #
443
+ # Example:
444
+ # rename_table('octopuses', 'octopi')
445
+ def rename_table(table_name, new_name)
446
+ exec_query "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
447
+ rename_table_indexes(table_name, new_name)
448
+ end
449
+
450
+ # See: http://www.sqlite.org/lang_altertable.html
451
+ # SQLite has an additional restriction on the ALTER TABLE statement
452
+ def valid_alter_table_type?(type)
453
+ type.to_sym != :primary_key
454
+ end
455
+
456
+ def add_column(table_name, column_name, type, options = {}) #:nodoc:
457
+ if valid_alter_table_type?(type)
458
+ super(table_name, column_name, type, options)
459
+ else
460
+ alter_table(table_name) do |definition|
461
+ definition.column(column_name, type, options)
462
+ end
463
+ end
464
+ end
465
+
466
+ def remove_column(table_name, column_name, type = nil, options = {}) #:nodoc:
467
+ alter_table(table_name) do |definition|
468
+ definition.remove_column column_name
469
+ end
470
+ end
471
+
472
+ def change_column_default(table_name, column_name, default) #:nodoc:
473
+ alter_table(table_name) do |definition|
474
+ definition[column_name].default = default
475
+ end
476
+ end
477
+
478
+ def change_column_null(table_name, column_name, null, default = nil)
479
+ unless null || default.nil?
480
+ exec_query("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
481
+ end
482
+ alter_table(table_name) do |definition|
483
+ definition[column_name].null = null
484
+ end
485
+ end
486
+
487
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
488
+ alter_table(table_name) do |definition|
489
+ include_default = options_include_default?(options)
490
+ definition[column_name].instance_eval do
491
+ self.type = type
492
+ self.limit = options[:limit] if options.include?(:limit)
493
+ self.default = options[:default] if include_default
494
+ self.null = options[:null] if options.include?(:null)
495
+ self.precision = options[:precision] if options.include?(:precision)
496
+ self.scale = options[:scale] if options.include?(:scale)
497
+ end
498
+ end
499
+ end
500
+
501
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
502
+ column = column_for(table_name, column_name)
503
+ alter_table(table_name, rename: {column.name => new_column_name.to_s})
504
+ rename_column_indexes(table_name, column.name, new_column_name)
505
+ end
506
+
507
+ protected
508
+
509
+ def initialize_type_map(m)
510
+ super
511
+ m.register_type(/binary/i, SQLite3Binary.new)
512
+ register_class_with_limit m, %r(char)i, SQLite3String
513
+ end
514
+
515
+ def table_structure(table_name)
516
+ structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA').to_hash
517
+ raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
518
+ structure
519
+ end
520
+
521
+ def alter_table(table_name, options = {}) #:nodoc:
522
+ altered_table_name = "a#{table_name}"
523
+ caller = lambda {|definition| yield definition if block_given?}
524
+
525
+ transaction do
526
+ move_table(table_name, altered_table_name,
527
+ options.merge(:temporary => true))
528
+ move_table(altered_table_name, table_name, &caller)
529
+ end
530
+ end
531
+
532
+ def move_table(from, to, options = {}, &block) #:nodoc:
533
+ copy_table(from, to, options, &block)
534
+ drop_table(from)
535
+ end
536
+
537
+ def copy_table(from, to, options = {}) #:nodoc:
538
+ from_primary_key = primary_key(from)
539
+ options[:id] = false
540
+ create_table(to, options) do |definition|
541
+ @definition = definition
542
+ @definition.primary_key(from_primary_key) if from_primary_key.present?
543
+ columns(from).each do |column|
544
+ column_name = options[:rename] ?
545
+ (options[:rename][column.name] ||
546
+ options[:rename][column.name.to_sym] ||
547
+ column.name) : column.name
548
+ next if column_name == from_primary_key
549
+
550
+ @definition.column(column_name, column.type,
551
+ :limit => column.limit, :default => column.default,
552
+ :precision => column.precision, :scale => column.scale,
553
+ :null => column.null)
554
+ end
555
+ yield @definition if block_given?
556
+ end
557
+ copy_table_indexes(from, to, options[:rename] || {})
558
+ copy_table_contents(from, to,
559
+ @definition.columns.map {|column| column.name},
560
+ options[:rename] || {})
561
+ end
562
+
563
+ def copy_table_indexes(from, to, rename = {}) #:nodoc:
564
+ indexes(from).each do |index|
565
+ name = index.name
566
+ if to == "a#{from}"
567
+ name = "t#{name}"
568
+ elsif from == "a#{to}"
569
+ name = name[1..-1]
570
+ end
571
+
572
+ to_column_names = columns(to).map { |c| c.name }
573
+ columns = index.columns.map {|c| rename[c] || c }.select do |column|
574
+ to_column_names.include?(column)
575
+ end
576
+
577
+ unless columns.empty?
578
+ # index name can't be the same
579
+ opts = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
580
+ opts[:unique] = true if index.unique
581
+ add_index(to, columns, opts)
582
+ end
583
+ end
584
+ end
585
+
586
+ def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
587
+ column_mappings = Hash[columns.map {|name| [name, name]}]
588
+ rename.each { |a| column_mappings[a.last] = a.first }
589
+ from_columns = columns(from).collect {|col| col.name}
590
+ columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
591
+ quoted_columns = columns.map { |col| quote_column_name(col) } * ','
592
+
593
+ quoted_to = quote_table_name(to)
594
+
595
+ raw_column_mappings = Hash[columns(from).map { |c| [c.name, c] }]
596
+
597
+ exec_query("SELECT * FROM #{quote_table_name(from)}").each do |row|
598
+ sql = "INSERT INTO #{quoted_to} (#{quoted_columns}) VALUES ("
599
+
600
+ column_values = columns.map do |col|
601
+ quote(row[column_mappings[col]], raw_column_mappings[col])
602
+ end
603
+
604
+ sql << column_values * ', '
605
+ sql << ')'
606
+ exec_query sql
607
+ end
608
+ end
609
+
610
+ def sqlite_version
611
+ @sqlite_version ||= SQLite3Adapter::Version.new(select_value('select sqlite_version(*)'))
612
+ end
613
+
614
+ def translate_exception(exception, message)
615
+ case exception.message
616
+ # SQLite 3.8.2 returns a newly formatted error message:
617
+ # UNIQUE constraint failed: *table_name*.*column_name*
618
+ # Older versions of SQLite return:
619
+ # column *column_name* is not unique
620
+ when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
621
+ RecordNotUnique.new(message, exception)
622
+ else
623
+ super
624
+ end
625
+ end
626
+ end
627
+ end
628
+ end