activerecord 4.2.0 → 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 +1537 -789
- 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 +16 -3
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +23 -9
- data/lib/active_record/associations/association_scope.rb +74 -102
- data/lib/active_record/associations/belongs_to_association.rb +26 -29
- 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 +12 -20
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +22 -15
- 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 +61 -33
- data/lib/active_record/associations/collection_proxy.rb +81 -35
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +21 -57
- data/lib/active_record/associations/has_many_through_association.rb +15 -45
- data/lib/active_record/associations/has_one_association.rb +13 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +20 -8
- data/lib/active_record/associations/join_dependency.rb +37 -21
- data/lib/active_record/associations/preloader/association.rb +51 -53
- 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 +18 -8
- data/lib/active_record/associations/singular_association.rb +8 -8
- data/lib/active_record/associations/through_association.rb +22 -9
- data/lib/active_record/associations.rb +321 -212
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute.rb +79 -15
- 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 +6 -1
- data/lib/active_record/attribute_methods/dirty.rb +51 -81
- 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 +65 -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 +37 -15
- data/lib/active_record/attribute_set.rb +34 -3
- data/lib/active_record/attributes.rb +199 -73
- data/lib/active_record/autosave_association.rb +73 -25
- data/lib/active_record/base.rb +35 -27
- 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 +457 -181
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -59
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -9
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -4
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +246 -185
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +438 -136
- data/lib/active_record/connection_adapters/abstract/transaction.rb +53 -40
- data/lib/active_record/connection_adapters/abstract_adapter.rb +166 -66
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +429 -335
- 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 +26 -177
- data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +11 -73
- 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 +2 -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 -13
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -1
- 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/type_map_initializer.rb +17 -5
- 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 +248 -154
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +258 -170
- 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 +150 -209
- data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
- data/lib/active_record/connection_handling.rb +38 -15
- data/lib/active_record/core.rb +109 -114
- data/lib/active_record/counter_cache.rb +14 -25
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +115 -79
- data/lib/active_record/errors.rb +88 -48
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +2 -2
- data/lib/active_record/fixture_set/file.rb +26 -5
- data/lib/active_record/fixtures.rb +84 -46
- data/lib/active_record/gem_version.rb +2 -2
- 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 +46 -0
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +27 -25
- 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 +372 -114
- data/lib/active_record/model_schema.rb +128 -38
- data/lib/active_record/nested_attributes.rb +71 -32
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/null_relation.rb +16 -8
- data/lib/active_record/persistence.rb +124 -80
- data/lib/active_record/query_cache.rb +15 -18
- data/lib/active_record/querying.rb +10 -9
- data/lib/active_record/railtie.rb +28 -19
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +67 -51
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +318 -139
- 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 +167 -97
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +38 -41
- data/lib/active_record/relation/predicate_builder/array_handler.rb +12 -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 +124 -82
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +323 -257
- data/lib/active_record/relation/record_fetch_warning.rb +49 -0
- data/lib/active_record/relation/spawn_methods.rb +11 -10
- 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 -115
- 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 +24 -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 +59 -42
- data/lib/active_record/tasks/mysql_database_tasks.rb +32 -26
- data/lib/active_record/tasks/postgresql_database_tasks.rb +29 -9
- 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 +159 -67
- 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 -38
- data/lib/active_record/type/hash_lookup_type_map.rb +8 -2
- 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 +21 -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 +29 -18
- data/lib/active_record/validations.rb +33 -32
- data/lib/active_record.rb +9 -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 -6
- data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -7
- 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 +60 -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 -30
- data/lib/active_record/type/decimal.rb +0 -40
- 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 -55
- 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 -36
- 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 -101
@@ -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,25 +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
|
-
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
45
|
# The SQLite3 adapter works SQLite 3.6.16 or newer
|
64
46
|
# with the sqlite3-ruby drivers (available as gem from https://rubygems.org/gems/sqlite3).
|
65
47
|
#
|
@@ -68,7 +50,8 @@ module ActiveRecord
|
|
68
50
|
# * <tt>:database</tt> - Path to the database file.
|
69
51
|
class SQLite3Adapter < AbstractAdapter
|
70
52
|
ADAPTER_NAME = 'SQLite'.freeze
|
71
|
-
|
53
|
+
|
54
|
+
include SQLite3::Quoting
|
72
55
|
|
73
56
|
NATIVE_DATABASE_TYPES = {
|
74
57
|
primary_key: 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL',
|
@@ -84,68 +67,27 @@ module ActiveRecord
|
|
84
67
|
boolean: { name: "boolean" }
|
85
68
|
}
|
86
69
|
|
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
70
|
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
71
|
private
|
125
|
-
def cache
|
126
|
-
@cache[$$]
|
127
|
-
end
|
128
72
|
|
129
73
|
def dealloc(stmt)
|
130
|
-
stmt.close unless stmt.closed?
|
74
|
+
stmt[:stmt].close unless stmt[:stmt].closed?
|
131
75
|
end
|
132
76
|
end
|
133
77
|
|
134
|
-
def
|
135
|
-
|
78
|
+
def schema_creation # :nodoc:
|
79
|
+
SQLite3::SchemaCreation.new self
|
80
|
+
end
|
136
81
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
@config = config
|
82
|
+
def arel_visitor # :nodoc:
|
83
|
+
Arel::Visitors::SQLite.new(self)
|
84
|
+
end
|
141
85
|
|
142
|
-
|
86
|
+
def initialize(connection, logger, connection_options, config)
|
87
|
+
super(connection, logger, config)
|
143
88
|
|
144
|
-
|
145
|
-
|
146
|
-
else
|
147
|
-
@prepared_statements = false
|
148
|
-
end
|
89
|
+
@active = nil
|
90
|
+
@statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
|
149
91
|
end
|
150
92
|
|
151
93
|
def supports_ddl_transactions?
|
@@ -183,6 +125,14 @@ module ActiveRecord
|
|
183
125
|
true
|
184
126
|
end
|
185
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
|
+
|
186
136
|
def active?
|
187
137
|
@active != false
|
188
138
|
end
|
@@ -204,6 +154,10 @@ module ActiveRecord
|
|
204
154
|
true
|
205
155
|
end
|
206
156
|
|
157
|
+
def valid_type?(type)
|
158
|
+
true
|
159
|
+
end
|
160
|
+
|
207
161
|
# Returns 62. SQLite supports index names up to 64
|
208
162
|
# characters. The rest is used by rails internally to perform
|
209
163
|
# temporary rename operations
|
@@ -224,82 +178,27 @@ module ActiveRecord
|
|
224
178
|
true
|
225
179
|
end
|
226
180
|
|
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
181
|
#--
|
270
182
|
# DATABASE STATEMENTS ======================================
|
271
183
|
#++
|
272
184
|
|
273
185
|
def explain(arel, binds = [])
|
274
186
|
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
|
187
|
+
SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', []))
|
290
188
|
end
|
291
189
|
|
292
|
-
def exec_query(sql, name = nil, binds = [])
|
293
|
-
type_casted_binds = binds.map { |
|
294
|
-
[col, type_cast(val, col)]
|
295
|
-
}
|
190
|
+
def exec_query(sql, name = nil, binds = [], prepare: false)
|
191
|
+
type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) }
|
296
192
|
|
297
|
-
log(sql, name,
|
193
|
+
log(sql, name, binds) do
|
298
194
|
# Don't cache statements if they are not prepared
|
299
|
-
|
195
|
+
unless prepare
|
300
196
|
stmt = @connection.prepare(sql)
|
301
197
|
begin
|
302
198
|
cols = stmt.columns
|
199
|
+
unless without_prepared_statement?(binds)
|
200
|
+
stmt.bind_params(type_casted_binds)
|
201
|
+
end
|
303
202
|
records = stmt.to_a
|
304
203
|
ensure
|
305
204
|
stmt.close
|
@@ -312,7 +211,7 @@ module ActiveRecord
|
|
312
211
|
stmt = cache[:stmt]
|
313
212
|
cols = cache[:cols] ||= stmt.columns
|
314
213
|
stmt.reset!
|
315
|
-
stmt.bind_params
|
214
|
+
stmt.bind_params(type_casted_binds)
|
316
215
|
end
|
317
216
|
|
318
217
|
ActiveRecord::Result.new(cols, stmt.to_a)
|
@@ -333,26 +232,6 @@ module ActiveRecord
|
|
333
232
|
log(sql, name) { @connection.execute(sql) }
|
334
233
|
end
|
335
234
|
|
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
235
|
def begin_db_transaction #:nodoc:
|
357
236
|
log('begin transaction',nil) { @connection.transaction }
|
358
237
|
end
|
@@ -361,31 +240,67 @@ module ActiveRecord
|
|
361
240
|
log('commit transaction',nil) { @connection.commit }
|
362
241
|
end
|
363
242
|
|
364
|
-
def
|
243
|
+
def exec_rollback_db_transaction #:nodoc:
|
365
244
|
log('rollback transaction',nil) { @connection.rollback }
|
366
245
|
end
|
367
246
|
|
368
247
|
# SCHEMA STATEMENTS ========================================
|
369
248
|
|
370
|
-
def tables(name = nil
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
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
|
377
255
|
|
378
|
-
|
379
|
-
|
256
|
+
if name
|
257
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
258
|
+
Passing arguments to #tables is deprecated without replacement.
|
259
|
+
MSG
|
380
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')
|
381
267
|
end
|
382
268
|
|
383
269
|
def table_exists?(table_name)
|
384
|
-
|
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?
|
385
299
|
end
|
386
300
|
|
387
301
|
# Returns an array of +Column+ objects for the table specified by +table_name+.
|
388
|
-
def columns(table_name)
|
302
|
+
def columns(table_name) # :nodoc:
|
303
|
+
table_name = table_name.to_s
|
389
304
|
table_structure(table_name).map do |field|
|
390
305
|
case field["dflt_value"]
|
391
306
|
when /^null$/i
|
@@ -396,9 +311,10 @@ module ActiveRecord
|
|
396
311
|
field["dflt_value"] = $1.gsub('""', '"')
|
397
312
|
end
|
398
313
|
|
314
|
+
collation = field['collation']
|
399
315
|
sql_type = field['type']
|
400
|
-
|
401
|
-
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)
|
402
318
|
end
|
403
319
|
end
|
404
320
|
|
@@ -427,14 +343,13 @@ module ActiveRecord
|
|
427
343
|
end
|
428
344
|
end
|
429
345
|
|
430
|
-
def
|
431
|
-
|
432
|
-
|
433
|
-
}
|
434
|
-
column && column['name']
|
346
|
+
def primary_keys(table_name) # :nodoc:
|
347
|
+
pks = table_structure(table_name).select { |f| f['pk'] > 0 }
|
348
|
+
pks.sort_by { |f| f['pk'] }.map { |f| f['name'] }
|
435
349
|
end
|
436
350
|
|
437
|
-
def remove_index
|
351
|
+
def remove_index(table_name, options = {}) #:nodoc:
|
352
|
+
index_name = index_name_for_remove(table_name, options)
|
438
353
|
exec_query "DROP INDEX #{quote_column_name(index_name)}"
|
439
354
|
end
|
440
355
|
|
@@ -469,13 +384,15 @@ module ActiveRecord
|
|
469
384
|
end
|
470
385
|
end
|
471
386
|
|
472
|
-
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
|
+
|
473
390
|
alter_table(table_name) do |definition|
|
474
391
|
definition[column_name].default = default
|
475
392
|
end
|
476
393
|
end
|
477
394
|
|
478
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
395
|
+
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
|
479
396
|
unless null || default.nil?
|
480
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")
|
481
398
|
end
|
@@ -494,6 +411,7 @@ module ActiveRecord
|
|
494
411
|
self.null = options[:null] if options.include?(:null)
|
495
412
|
self.precision = options[:precision] if options.include?(:precision)
|
496
413
|
self.scale = options[:scale] if options.include?(:scale)
|
414
|
+
self.collation = options[:collation] if options.include?(:collation)
|
497
415
|
end
|
498
416
|
end
|
499
417
|
end
|
@@ -506,16 +424,10 @@ module ActiveRecord
|
|
506
424
|
|
507
425
|
protected
|
508
426
|
|
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
427
|
def table_structure(table_name)
|
516
|
-
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')
|
517
429
|
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
|
518
|
-
structure
|
430
|
+
table_structure_with_collation(table_name, structure)
|
519
431
|
end
|
520
432
|
|
521
433
|
def alter_table(table_name, options = {}) #:nodoc:
|
@@ -550,13 +462,13 @@ module ActiveRecord
|
|
550
462
|
@definition.column(column_name, column.type,
|
551
463
|
:limit => column.limit, :default => column.default,
|
552
464
|
:precision => column.precision, :scale => column.scale,
|
553
|
-
:null => column.null)
|
465
|
+
:null => column.null, collation: column.collation)
|
554
466
|
end
|
555
467
|
yield @definition if block_given?
|
556
468
|
end
|
557
469
|
copy_table_indexes(from, to, options[:rename] || {})
|
558
470
|
copy_table_contents(from, to,
|
559
|
-
@definition.columns.map
|
471
|
+
@definition.columns.map(&:name),
|
560
472
|
options[:rename] || {})
|
561
473
|
end
|
562
474
|
|
@@ -569,7 +481,7 @@ module ActiveRecord
|
|
569
481
|
name = name[1..-1]
|
570
482
|
end
|
571
483
|
|
572
|
-
to_column_names = columns(to).map
|
484
|
+
to_column_names = columns(to).map(&:name)
|
573
485
|
columns = index.columns.map {|c| rename[c] || c }.select do |column|
|
574
486
|
to_column_names.include?(column)
|
575
487
|
end
|
@@ -586,25 +498,14 @@ module ActiveRecord
|
|
586
498
|
def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
|
587
499
|
column_mappings = Hash[columns.map {|name| [name, name]}]
|
588
500
|
rename.each { |a| column_mappings[a.last] = a.first }
|
589
|
-
from_columns = columns(from).collect
|
501
|
+
from_columns = columns(from).collect(&:name)
|
590
502
|
columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
|
503
|
+
from_columns_to_copy = columns.map { |col| column_mappings[col] }
|
591
504
|
quoted_columns = columns.map { |col| quote_column_name(col) } * ','
|
505
|
+
quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ','
|
592
506
|
|
593
|
-
|
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
|
507
|
+
exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
|
508
|
+
SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
|
608
509
|
end
|
609
510
|
|
610
511
|
def sqlite_version
|
@@ -618,11 +519,51 @@ module ActiveRecord
|
|
618
519
|
# Older versions of SQLite return:
|
619
520
|
# column *column_name* is not unique
|
620
521
|
when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
|
621
|
-
RecordNotUnique.new(message
|
522
|
+
RecordNotUnique.new(message)
|
622
523
|
else
|
623
524
|
super
|
624
525
|
end
|
625
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
|
626
567
|
end
|
627
568
|
end
|
628
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
|