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