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