activerecord 4.2.11.3 → 5.0.0.beta1
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 +1029 -1349
- data/MIT-LICENSE +1 -1
- data/README.rdoc +6 -7
- data/examples/performance.rb +2 -2
- data/lib/active_record.rb +7 -3
- data/lib/active_record/aggregations.rb +35 -25
- data/lib/active_record/association_relation.rb +2 -2
- data/lib/active_record/associations.rb +305 -204
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +10 -8
- data/lib/active_record/associations/association_scope.rb +73 -102
- data/lib/active_record/associations/belongs_to_association.rb +20 -32
- data/lib/active_record/associations/builder/association.rb +28 -34
- data/lib/active_record/associations/builder/belongs_to.rb +41 -18
- data/lib/active_record/associations/builder/collection_association.rb +8 -24
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +11 -11
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +10 -5
- data/lib/active_record/associations/builder/singular_association.rb +2 -9
- data/lib/active_record/associations/collection_association.rb +40 -43
- data/lib/active_record/associations/collection_proxy.rb +55 -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 -52
- data/lib/active_record/associations/has_one_association.rb +12 -5
- data/lib/active_record/associations/join_dependency.rb +28 -18
- data/lib/active_record/associations/join_dependency/join_association.rb +13 -12
- data/lib/active_record/associations/preloader.rb +13 -4
- data/lib/active_record/associations/preloader/association.rb +45 -51
- 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 +5 -4
- data/lib/active_record/associations/singular_association.rb +6 -0
- data/lib/active_record/associations/through_association.rb +11 -3
- data/lib/active_record/attribute.rb +61 -17
- data/lib/active_record/attribute/user_provided_default.rb +23 -0
- data/lib/active_record/attribute_assignment.rb +27 -140
- data/lib/active_record/attribute_decorators.rb +6 -5
- data/lib/active_record/attribute_methods.rb +79 -26
- 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 +26 -42
- data/lib/active_record/attribute_methods/serialization.rb +13 -16
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +42 -9
- data/lib/active_record/attribute_methods/write.rb +13 -24
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set.rb +30 -3
- data/lib/active_record/attribute_set/builder.rb +6 -4
- data/lib/active_record/attributes.rb +194 -81
- data/lib/active_record/autosave_association.rb +33 -15
- data/lib/active_record/base.rb +30 -18
- data/lib/active_record/callbacks.rb +36 -40
- data/lib/active_record/coders/yaml_column.rb +20 -8
- data/lib/active_record/collection_cache_key.rb +31 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +431 -122
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +40 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +62 -8
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -38
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +229 -185
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +52 -13
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +275 -115
- data/lib/active_record/connection_adapters/abstract/transaction.rb +32 -33
- data/lib/active_record/connection_adapters/abstract_adapter.rb +83 -32
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +384 -221
- data/lib/active_record/connection_adapters/column.rb +27 -41
- data/lib/active_record/connection_adapters/connection_specification.rb +2 -21
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +57 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +69 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +59 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +22 -101
- data/lib/active_record/connection_adapters/postgresql/column.rb +6 -10
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +23 -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 +1 -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 -2
- 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 +23 -16
- 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/quoting.rb +18 -11
- 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 +54 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +174 -128
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +184 -112
- 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/schema_creation.rb +15 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +134 -110
- data/lib/active_record/connection_adapters/statement_pool.rb +28 -11
- data/lib/active_record/connection_handling.rb +5 -5
- data/lib/active_record/core.rb +72 -104
- data/lib/active_record/counter_cache.rb +9 -20
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +110 -76
- data/lib/active_record/errors.rb +72 -47
- 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 +19 -4
- data/lib/active_record/fixtures.rb +76 -40
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +27 -40
- data/lib/active_record/integration.rb +4 -4
- 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 +10 -14
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +40 -22
- data/lib/active_record/migration.rb +304 -133
- data/lib/active_record/migration/command_recorder.rb +59 -18
- data/lib/active_record/migration/compatibility.rb +90 -0
- data/lib/active_record/model_schema.rb +92 -40
- data/lib/active_record/nested_attributes.rb +45 -34
- data/lib/active_record/null_relation.rb +15 -7
- data/lib/active_record/persistence.rb +112 -72
- data/lib/active_record/querying.rb +6 -5
- data/lib/active_record/railtie.rb +20 -13
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +47 -38
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +182 -57
- data/lib/active_record/relation.rb +152 -100
- data/lib/active_record/relation/batches.rb +133 -33
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/calculations.rb +80 -101
- data/lib/active_record/relation/delegation.rb +6 -19
- data/lib/active_record/relation/finder_methods.rb +58 -46
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +13 -42
- data/lib/active_record/relation/predicate_builder.rb +99 -105
- data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +78 -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/range_handler.rb +17 -0
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +274 -238
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +3 -6
- data/lib/active_record/relation/where_clause.rb +173 -0
- data/lib/active_record/relation/where_clause_factory.rb +37 -0
- data/lib/active_record/result.rb +4 -3
- data/lib/active_record/runtime_registry.rb +1 -1
- data/lib/active_record/sanitization.rb +94 -65
- data/lib/active_record/schema.rb +23 -22
- data/lib/active_record/schema_dumper.rb +33 -22
- data/lib/active_record/schema_migration.rb +10 -4
- data/lib/active_record/scoping.rb +17 -6
- data/lib/active_record/scoping/default.rb +19 -6
- data/lib/active_record/scoping/named.rb +39 -28
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +2 -4
- data/lib/active_record/statement_cache.rb +15 -13
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +54 -0
- data/lib/active_record/table_metadata.rb +64 -0
- data/lib/active_record/tasks/database_tasks.rb +30 -40
- data/lib/active_record/tasks/mysql_database_tasks.rb +7 -15
- 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 +16 -9
- data/lib/active_record/touch_later.rb +58 -0
- data/lib/active_record/transactions.rb +138 -56
- data/lib/active_record/type.rb +66 -17
- 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 +33 -0
- data/lib/active_record/type/internal/timezone.rb +15 -0
- data/lib/active_record/type/serialized.rb +9 -14
- data/lib/active_record/type/time.rb +3 -21
- data/lib/active_record/type/type_map.rb +4 -4
- data/lib/active_record/type_caster.rb +7 -0
- data/lib/active_record/type_caster/connection.rb +29 -0
- data/lib/active_record/type_caster/map.rb +19 -0
- data/lib/active_record/validations.rb +33 -32
- data/lib/active_record/validations/absence.rb +24 -0
- data/lib/active_record/validations/associated.rb +10 -3
- data/lib/active_record/validations/length.rb +36 -0
- data/lib/active_record/validations/presence.rb +12 -12
- data/lib/active_record/validations/uniqueness.rb +24 -21
- data/lib/rails/generators/active_record/migration.rb +7 -0
- 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 +4 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +21 -15
- data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
- metadata +50 -35
- 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
@@ -10,33 +10,46 @@ module ActiveRecord
|
|
10
10
|
@columns = {}
|
11
11
|
@columns_hash = {}
|
12
12
|
@primary_keys = {}
|
13
|
-
@
|
13
|
+
@data_sources = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize_dup(other)
|
17
|
+
super
|
18
|
+
@columns = @columns.dup
|
19
|
+
@columns_hash = @columns_hash.dup
|
20
|
+
@primary_keys = @primary_keys.dup
|
21
|
+
@data_sources = @data_sources.dup
|
14
22
|
end
|
15
23
|
|
16
24
|
def primary_keys(table_name)
|
17
|
-
@primary_keys[table_name] ||=
|
25
|
+
@primary_keys[table_name] ||= data_source_exists?(table_name) ? connection.primary_key(table_name) : nil
|
18
26
|
end
|
19
27
|
|
20
28
|
# A cached lookup for table existence.
|
21
|
-
def
|
22
|
-
|
23
|
-
return @
|
29
|
+
def data_source_exists?(name)
|
30
|
+
prepare_data_sources if @data_sources.empty?
|
31
|
+
return @data_sources[name] if @data_sources.key? name
|
24
32
|
|
25
|
-
@
|
33
|
+
@data_sources[name] = connection.data_source_exists?(name)
|
26
34
|
end
|
35
|
+
alias table_exists? data_source_exists?
|
36
|
+
deprecate :table_exists? => "use #data_source_exists? instead"
|
37
|
+
|
27
38
|
|
28
39
|
# Add internal cache for table with +table_name+.
|
29
40
|
def add(table_name)
|
30
|
-
if
|
41
|
+
if data_source_exists?(table_name)
|
31
42
|
primary_keys(table_name)
|
32
43
|
columns(table_name)
|
33
44
|
columns_hash(table_name)
|
34
45
|
end
|
35
46
|
end
|
36
47
|
|
37
|
-
def
|
38
|
-
@
|
48
|
+
def data_sources(name)
|
49
|
+
@data_sources[name]
|
39
50
|
end
|
51
|
+
alias tables data_sources
|
52
|
+
deprecate :tables => "use #data_sources instead"
|
40
53
|
|
41
54
|
# Get the columns for a table
|
42
55
|
def columns(table_name)
|
@@ -56,38 +69,38 @@ module ActiveRecord
|
|
56
69
|
@columns.clear
|
57
70
|
@columns_hash.clear
|
58
71
|
@primary_keys.clear
|
59
|
-
@
|
72
|
+
@data_sources.clear
|
60
73
|
@version = nil
|
61
74
|
end
|
62
75
|
|
63
76
|
def size
|
64
|
-
[@columns, @columns_hash, @primary_keys, @
|
65
|
-
x.size
|
66
|
-
}.inject :+
|
77
|
+
[@columns, @columns_hash, @primary_keys, @data_sources].map(&:size).inject :+
|
67
78
|
end
|
68
79
|
|
69
|
-
# Clear out internal caches for
|
70
|
-
def
|
71
|
-
@columns.delete
|
72
|
-
@columns_hash.delete
|
73
|
-
@primary_keys.delete
|
74
|
-
@
|
80
|
+
# Clear out internal caches for the data source +name+.
|
81
|
+
def clear_data_source_cache!(name)
|
82
|
+
@columns.delete name
|
83
|
+
@columns_hash.delete name
|
84
|
+
@primary_keys.delete name
|
85
|
+
@data_sources.delete name
|
75
86
|
end
|
87
|
+
alias clear_table_cache! clear_data_source_cache!
|
88
|
+
deprecate :clear_table_cache! => "use #clear_data_source_cache! instead"
|
76
89
|
|
77
90
|
def marshal_dump
|
78
91
|
# if we get current version during initialization, it happens stack over flow.
|
79
92
|
@version = ActiveRecord::Migrator.current_version
|
80
|
-
[@version, @columns, @columns_hash, @primary_keys, @
|
93
|
+
[@version, @columns, @columns_hash, @primary_keys, @data_sources]
|
81
94
|
end
|
82
95
|
|
83
96
|
def marshal_load(array)
|
84
|
-
@version, @columns, @columns_hash, @primary_keys, @
|
97
|
+
@version, @columns, @columns_hash, @primary_keys, @data_sources = array
|
85
98
|
end
|
86
99
|
|
87
100
|
private
|
88
101
|
|
89
|
-
def
|
90
|
-
connection.
|
102
|
+
def prepare_data_sources
|
103
|
+
connection.data_sources.each { |source| @data_sources[source] = true }
|
91
104
|
end
|
92
105
|
end
|
93
106
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
# :stopdoc:
|
3
|
+
module ConnectionAdapters
|
4
|
+
class SqlTypeMetadata
|
5
|
+
attr_reader :sql_type, :type, :limit, :precision, :scale
|
6
|
+
|
7
|
+
def initialize(sql_type: nil, type: nil, limit: nil, precision: nil, scale: nil)
|
8
|
+
@sql_type = sql_type
|
9
|
+
@type = type
|
10
|
+
@limit = limit
|
11
|
+
@precision = precision
|
12
|
+
@scale = scale
|
13
|
+
end
|
14
|
+
|
15
|
+
def ==(other)
|
16
|
+
other.is_a?(SqlTypeMetadata) &&
|
17
|
+
attributes_for_hash == other.attributes_for_hash
|
18
|
+
end
|
19
|
+
alias eql? ==
|
20
|
+
|
21
|
+
def hash
|
22
|
+
attributes_for_hash.hash
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
|
27
|
+
def attributes_for_hash
|
28
|
+
[self.class, sql_type, type, limit, precision, scale]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module SQLite3
|
4
|
+
class SchemaCreation < AbstractAdapter::SchemaCreation
|
5
|
+
private
|
6
|
+
def add_column_options!(sql, options)
|
7
|
+
if options[:collation]
|
8
|
+
sql << " COLLATE \"#{options[:collation]}\""
|
9
|
+
end
|
10
|
+
super
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -1,6 +1,6 @@
|
|
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/schema_creation'
|
4
4
|
|
5
5
|
gem 'sqlite3', '~> 1.3.6'
|
6
6
|
require 'sqlite3'
|
@@ -33,7 +33,7 @@ module ActiveRecord
|
|
33
33
|
ConnectionAdapters::SQLite3Adapter.new(db, logger, nil, config)
|
34
34
|
rescue Errno::ENOENT => error
|
35
35
|
if error.message.include?("No such file or directory")
|
36
|
-
raise ActiveRecord::NoDatabaseError
|
36
|
+
raise ActiveRecord::NoDatabaseError
|
37
37
|
else
|
38
38
|
raise
|
39
39
|
end
|
@@ -41,15 +41,6 @@ module ActiveRecord
|
|
41
41
|
end
|
42
42
|
|
43
43
|
module ConnectionAdapters #:nodoc:
|
44
|
-
class SQLite3Binary < Type::Binary # :nodoc:
|
45
|
-
def cast_value(value)
|
46
|
-
if value.encoding != Encoding::ASCII_8BIT
|
47
|
-
value = value.force_encoding(Encoding::ASCII_8BIT)
|
48
|
-
end
|
49
|
-
value
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
44
|
# The SQLite3 adapter works SQLite 3.6.16 or newer
|
54
45
|
# with the sqlite3-ruby drivers (available as gem from https://rubygems.org/gems/sqlite3).
|
55
46
|
#
|
@@ -75,52 +66,29 @@ module ActiveRecord
|
|
75
66
|
}
|
76
67
|
|
77
68
|
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
69
|
private
|
103
|
-
def cache
|
104
|
-
@cache[$$]
|
105
|
-
end
|
106
70
|
|
107
71
|
def dealloc(stmt)
|
108
|
-
stmt.close unless stmt.closed?
|
72
|
+
stmt[:stmt].close unless stmt[:stmt].closed?
|
109
73
|
end
|
110
74
|
end
|
111
75
|
|
76
|
+
def schema_creation # :nodoc:
|
77
|
+
SQLite3::SchemaCreation.new self
|
78
|
+
end
|
79
|
+
|
112
80
|
def initialize(connection, logger, connection_options, config)
|
113
|
-
super(connection, logger)
|
81
|
+
super(connection, logger, config)
|
114
82
|
|
115
83
|
@active = nil
|
116
|
-
@statements = StatementPool.new(
|
117
|
-
self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 }))
|
118
|
-
@config = config
|
84
|
+
@statements = StatementPool.new(self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 }))
|
119
85
|
|
120
86
|
@visitor = Arel::Visitors::SQLite.new self
|
87
|
+
@quoted_column_names = {}
|
121
88
|
|
122
89
|
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
|
123
90
|
@prepared_statements = true
|
91
|
+
@visitor.extend(DetermineIfPreparableVisitor)
|
124
92
|
else
|
125
93
|
@prepared_statements = false
|
126
94
|
end
|
@@ -161,6 +129,10 @@ module ActiveRecord
|
|
161
129
|
true
|
162
130
|
end
|
163
131
|
|
132
|
+
def supports_datetime_with_precision?
|
133
|
+
true
|
134
|
+
end
|
135
|
+
|
164
136
|
def active?
|
165
137
|
@active != false
|
166
138
|
end
|
@@ -237,17 +209,7 @@ module ActiveRecord
|
|
237
209
|
end
|
238
210
|
|
239
211
|
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
|
212
|
+
@quoted_column_names[name] ||= %Q("#{name.to_s.gsub('"', '""')}")
|
251
213
|
end
|
252
214
|
|
253
215
|
#--
|
@@ -260,7 +222,7 @@ module ActiveRecord
|
|
260
222
|
end
|
261
223
|
|
262
224
|
class ExplainPrettyPrinter
|
263
|
-
# Pretty prints the result of
|
225
|
+
# Pretty prints the result of an EXPLAIN QUERY PLAN in a way that resembles
|
264
226
|
# the output of the SQLite shell:
|
265
227
|
#
|
266
228
|
# 0|0|0|SEARCH TABLE users USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
|
@@ -273,17 +235,18 @@ module ActiveRecord
|
|
273
235
|
end
|
274
236
|
end
|
275
237
|
|
276
|
-
def exec_query(sql, name = nil, binds = [])
|
277
|
-
type_casted_binds = binds.map { |
|
278
|
-
[col, type_cast(val, col)]
|
279
|
-
}
|
238
|
+
def exec_query(sql, name = nil, binds = [], prepare: false)
|
239
|
+
type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) }
|
280
240
|
|
281
|
-
log(sql, name,
|
241
|
+
log(sql, name, binds) do
|
282
242
|
# Don't cache statements if they are not prepared
|
283
|
-
|
243
|
+
unless prepare
|
284
244
|
stmt = @connection.prepare(sql)
|
285
245
|
begin
|
286
246
|
cols = stmt.columns
|
247
|
+
unless without_prepared_statement?(binds)
|
248
|
+
stmt.bind_params(type_casted_binds)
|
249
|
+
end
|
287
250
|
records = stmt.to_a
|
288
251
|
ensure
|
289
252
|
stmt.close
|
@@ -296,7 +259,7 @@ module ActiveRecord
|
|
296
259
|
stmt = cache[:stmt]
|
297
260
|
cols = cache[:cols] ||= stmt.columns
|
298
261
|
stmt.reset!
|
299
|
-
stmt.bind_params
|
262
|
+
stmt.bind_params(type_casted_binds)
|
300
263
|
end
|
301
264
|
|
302
265
|
ActiveRecord::Result.new(cols, stmt.to_a)
|
@@ -351,24 +314,57 @@ module ActiveRecord
|
|
351
314
|
|
352
315
|
# SCHEMA STATEMENTS ========================================
|
353
316
|
|
354
|
-
def tables(name = nil
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
317
|
+
def tables(name = nil) # :nodoc:
|
318
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
319
|
+
#tables currently returns both tables and views.
|
320
|
+
This behavior is deprecated and will be changed with Rails 5.1 to only return tables.
|
321
|
+
Use #data_sources instead.
|
322
|
+
MSG
|
323
|
+
|
324
|
+
if name
|
325
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
326
|
+
Passing arguments to #tables is deprecated without replacement.
|
327
|
+
MSG
|
364
328
|
end
|
329
|
+
|
330
|
+
data_sources
|
331
|
+
end
|
332
|
+
|
333
|
+
def data_sources
|
334
|
+
select_values("SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name <> 'sqlite_sequence'", 'SCHEMA')
|
365
335
|
end
|
366
|
-
alias data_sources tables
|
367
336
|
|
368
337
|
def table_exists?(table_name)
|
369
|
-
|
338
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
339
|
+
#table_exists? currently checks both tables and views.
|
340
|
+
This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
|
341
|
+
Use #data_source_exists? instead.
|
342
|
+
MSG
|
343
|
+
|
344
|
+
data_source_exists?(table_name)
|
345
|
+
end
|
346
|
+
|
347
|
+
def data_source_exists?(table_name)
|
348
|
+
return false unless table_name.present?
|
349
|
+
|
350
|
+
sql = "SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name <> 'sqlite_sequence'"
|
351
|
+
sql << " AND name = #{quote(table_name)}"
|
352
|
+
|
353
|
+
select_values(sql, 'SCHEMA').any?
|
354
|
+
end
|
355
|
+
|
356
|
+
def views # :nodoc:
|
357
|
+
select_values("SELECT name FROM sqlite_master WHERE type = 'view' AND name <> 'sqlite_sequence'", 'SCHEMA')
|
358
|
+
end
|
359
|
+
|
360
|
+
def view_exists?(view_name) # :nodoc:
|
361
|
+
return false unless view_name.present?
|
362
|
+
|
363
|
+
sql = "SELECT name FROM sqlite_master WHERE type = 'view' AND name <> 'sqlite_sequence'"
|
364
|
+
sql << " AND name = #{quote(view_name)}"
|
365
|
+
|
366
|
+
select_values(sql, 'SCHEMA').any?
|
370
367
|
end
|
371
|
-
alias data_source_exists? table_exists?
|
372
368
|
|
373
369
|
# Returns an array of +Column+ objects for the table specified by +table_name+.
|
374
370
|
def columns(table_name) #:nodoc:
|
@@ -382,9 +378,10 @@ module ActiveRecord
|
|
382
378
|
field["dflt_value"] = $1.gsub('""', '"')
|
383
379
|
end
|
384
380
|
|
381
|
+
collation = field['collation']
|
385
382
|
sql_type = field['type']
|
386
|
-
|
387
|
-
new_column(field['name'], field['dflt_value'],
|
383
|
+
type_metadata = fetch_type_metadata(sql_type)
|
384
|
+
new_column(field['name'], field['dflt_value'], type_metadata, field['notnull'].to_i == 0, nil, collation)
|
388
385
|
end
|
389
386
|
end
|
390
387
|
|
@@ -413,13 +410,13 @@ module ActiveRecord
|
|
413
410
|
end
|
414
411
|
end
|
415
412
|
|
416
|
-
def
|
413
|
+
def primary_keys(table_name) # :nodoc:
|
417
414
|
pks = table_structure(table_name).select { |f| f['pk'] > 0 }
|
418
|
-
|
419
|
-
pks[0]['name']
|
415
|
+
pks.sort_by { |f| f['pk'] }.map { |f| f['name'] }
|
420
416
|
end
|
421
417
|
|
422
|
-
def remove_index
|
418
|
+
def remove_index(table_name, options = {}) #:nodoc:
|
419
|
+
index_name = index_name_for_remove(table_name, options)
|
423
420
|
exec_query "DROP INDEX #{quote_column_name(index_name)}"
|
424
421
|
end
|
425
422
|
|
@@ -454,13 +451,15 @@ module ActiveRecord
|
|
454
451
|
end
|
455
452
|
end
|
456
453
|
|
457
|
-
def change_column_default(table_name, column_name,
|
454
|
+
def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
|
455
|
+
default = extract_new_default_value(default_or_changes)
|
456
|
+
|
458
457
|
alter_table(table_name) do |definition|
|
459
458
|
definition[column_name].default = default
|
460
459
|
end
|
461
460
|
end
|
462
461
|
|
463
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
462
|
+
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
|
464
463
|
unless null || default.nil?
|
465
464
|
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
465
|
end
|
@@ -479,6 +478,7 @@ module ActiveRecord
|
|
479
478
|
self.null = options[:null] if options.include?(:null)
|
480
479
|
self.precision = options[:precision] if options.include?(:precision)
|
481
480
|
self.scale = options[:scale] if options.include?(:scale)
|
481
|
+
self.collation = options[:collation] if options.include?(:collation)
|
482
482
|
end
|
483
483
|
end
|
484
484
|
end
|
@@ -491,15 +491,10 @@ module ActiveRecord
|
|
491
491
|
|
492
492
|
protected
|
493
493
|
|
494
|
-
def initialize_type_map(m)
|
495
|
-
super
|
496
|
-
m.register_type(/binary/i, SQLite3Binary.new)
|
497
|
-
end
|
498
|
-
|
499
494
|
def table_structure(table_name)
|
500
|
-
structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA')
|
495
|
+
structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA')
|
501
496
|
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
|
502
|
-
structure
|
497
|
+
table_structure_with_collation(table_name, structure)
|
503
498
|
end
|
504
499
|
|
505
500
|
def alter_table(table_name, options = {}) #:nodoc:
|
@@ -534,13 +529,13 @@ module ActiveRecord
|
|
534
529
|
@definition.column(column_name, column.type,
|
535
530
|
:limit => column.limit, :default => column.default,
|
536
531
|
:precision => column.precision, :scale => column.scale,
|
537
|
-
:null => column.null)
|
532
|
+
:null => column.null, collation: column.collation)
|
538
533
|
end
|
539
534
|
yield @definition if block_given?
|
540
535
|
end
|
541
536
|
copy_table_indexes(from, to, options[:rename] || {})
|
542
537
|
copy_table_contents(from, to,
|
543
|
-
@definition.columns.map
|
538
|
+
@definition.columns.map(&:name),
|
544
539
|
options[:rename] || {})
|
545
540
|
end
|
546
541
|
|
@@ -553,7 +548,7 @@ module ActiveRecord
|
|
553
548
|
name = name[1..-1]
|
554
549
|
end
|
555
550
|
|
556
|
-
to_column_names = columns(to).map
|
551
|
+
to_column_names = columns(to).map(&:name)
|
557
552
|
columns = index.columns.map {|c| rename[c] || c }.select do |column|
|
558
553
|
to_column_names.include?(column)
|
559
554
|
end
|
@@ -570,25 +565,14 @@ module ActiveRecord
|
|
570
565
|
def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
|
571
566
|
column_mappings = Hash[columns.map {|name| [name, name]}]
|
572
567
|
rename.each { |a| column_mappings[a.last] = a.first }
|
573
|
-
from_columns = columns(from).collect
|
568
|
+
from_columns = columns(from).collect(&:name)
|
574
569
|
columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
|
570
|
+
from_columns_to_copy = columns.map { |col| column_mappings[col] }
|
575
571
|
quoted_columns = columns.map { |col| quote_column_name(col) } * ','
|
572
|
+
quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ','
|
576
573
|
|
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
|
574
|
+
exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
|
575
|
+
SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
|
592
576
|
end
|
593
577
|
|
594
578
|
def sqlite_version
|
@@ -602,11 +586,51 @@ module ActiveRecord
|
|
602
586
|
# Older versions of SQLite return:
|
603
587
|
# column *column_name* is not unique
|
604
588
|
when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
|
605
|
-
RecordNotUnique.new(message
|
589
|
+
RecordNotUnique.new(message)
|
606
590
|
else
|
607
591
|
super
|
608
592
|
end
|
609
593
|
end
|
594
|
+
|
595
|
+
private
|
596
|
+
COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze
|
597
|
+
|
598
|
+
def table_structure_with_collation(table_name, basic_structure)
|
599
|
+
collation_hash = {}
|
600
|
+
sql = "SELECT sql FROM
|
601
|
+
(SELECT * FROM sqlite_master UNION ALL
|
602
|
+
SELECT * FROM sqlite_temp_master)
|
603
|
+
WHERE type='table' and name='#{ table_name }' \;"
|
604
|
+
|
605
|
+
# Result will have following sample string
|
606
|
+
# CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
607
|
+
# "password_digest" varchar COLLATE "NOCASE");
|
608
|
+
result = exec_query(sql, 'SCHEMA').first
|
609
|
+
|
610
|
+
if result
|
611
|
+
# Splitting with left parantheses and picking up last will return all
|
612
|
+
# columns separated with comma(,).
|
613
|
+
columns_string = result["sql"].split('(').last
|
614
|
+
|
615
|
+
columns_string.split(',').each do |column_string|
|
616
|
+
# This regex will match the column name and collation type and will save
|
617
|
+
# the value in $1 and $2 respectively.
|
618
|
+
collation_hash[$1] = $2 if (COLLATE_REGEX =~ column_string)
|
619
|
+
end
|
620
|
+
|
621
|
+
basic_structure.map! do |column|
|
622
|
+
column_name = column['name']
|
623
|
+
|
624
|
+
if collation_hash.has_key? column_name
|
625
|
+
column['collation'] = collation_hash[column_name]
|
626
|
+
end
|
627
|
+
|
628
|
+
column
|
629
|
+
end
|
630
|
+
else
|
631
|
+
basic_structure.to_hash
|
632
|
+
end
|
633
|
+
end
|
610
634
|
end
|
611
635
|
end
|
612
636
|
end
|