activerecord 4.2.9 → 5.2.8
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 +614 -1572
- data/MIT-LICENSE +2 -2
- data/README.rdoc +10 -11
- data/examples/performance.rb +32 -31
- data/examples/simple.rb +5 -4
- data/lib/active_record/aggregations.rb +263 -249
- data/lib/active_record/association_relation.rb +11 -6
- data/lib/active_record/associations/alias_tracker.rb +29 -35
- data/lib/active_record/associations/association.rb +77 -43
- data/lib/active_record/associations/association_scope.rb +106 -133
- data/lib/active_record/associations/belongs_to_association.rb +52 -41
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
- data/lib/active_record/associations/builder/association.rb +29 -38
- data/lib/active_record/associations/builder/belongs_to.rb +77 -30
- data/lib/active_record/associations/builder/collection_association.rb +9 -22
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +42 -35
- data/lib/active_record/associations/builder/has_many.rb +6 -4
- data/lib/active_record/associations/builder/has_one.rb +13 -6
- data/lib/active_record/associations/builder/singular_association.rb +15 -11
- data/lib/active_record/associations/collection_association.rb +139 -280
- data/lib/active_record/associations/collection_proxy.rb +231 -133
- data/lib/active_record/associations/foreign_association.rb +3 -1
- data/lib/active_record/associations/has_many_association.rb +34 -89
- data/lib/active_record/associations/has_many_through_association.rb +49 -76
- data/lib/active_record/associations/has_one_association.rb +38 -24
- data/lib/active_record/associations/has_one_through_association.rb +18 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +40 -89
- data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
- data/lib/active_record/associations/join_dependency.rb +133 -159
- data/lib/active_record/associations/preloader/association.rb +85 -120
- data/lib/active_record/associations/preloader/through_association.rb +85 -74
- data/lib/active_record/associations/preloader.rb +81 -91
- data/lib/active_record/associations/singular_association.rb +27 -34
- data/lib/active_record/associations/through_association.rb +38 -18
- data/lib/active_record/associations.rb +1732 -1597
- data/lib/active_record/attribute_assignment.rb +58 -182
- data/lib/active_record/attribute_decorators.rb +39 -15
- data/lib/active_record/attribute_methods/before_type_cast.rb +10 -8
- data/lib/active_record/attribute_methods/dirty.rb +94 -135
- data/lib/active_record/attribute_methods/primary_key.rb +86 -71
- data/lib/active_record/attribute_methods/query.rb +4 -2
- data/lib/active_record/attribute_methods/read.rb +45 -63
- data/lib/active_record/attribute_methods/serialization.rb +40 -20
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
- data/lib/active_record/attribute_methods/write.rb +30 -45
- data/lib/active_record/attribute_methods.rb +166 -109
- data/lib/active_record/attributes.rb +201 -82
- data/lib/active_record/autosave_association.rb +94 -36
- data/lib/active_record/base.rb +57 -44
- data/lib/active_record/callbacks.rb +97 -57
- data/lib/active_record/coders/json.rb +3 -1
- data/lib/active_record/coders/yaml_column.rb +24 -12
- data/lib/active_record/collection_cache_key.rb +53 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -290
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +237 -90
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +71 -21
- data/lib/active_record/connection_adapters/abstract/quoting.rb +118 -52
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +318 -217
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +570 -228
- data/lib/active_record/connection_adapters/abstract/transaction.rb +138 -70
- data/lib/active_record/connection_adapters/abstract_adapter.rb +325 -202
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +542 -593
- data/lib/active_record/connection_adapters/column.rb +50 -41
- data/lib/active_record/connection_adapters/connection_specification.rb +147 -135
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +41 -188
- data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +45 -114
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -58
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +10 -6
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
- data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +55 -53
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +107 -47
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +144 -90
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -284
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +432 -323
- data/lib/active_record/connection_adapters/schema_cache.rb +48 -24
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +269 -308
- data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
- data/lib/active_record/connection_handling.rb +40 -27
- data/lib/active_record/core.rb +178 -198
- data/lib/active_record/counter_cache.rb +79 -36
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +87 -105
- data/lib/active_record/enum.rb +135 -88
- data/lib/active_record/errors.rb +179 -52
- data/lib/active_record/explain.rb +23 -11
- data/lib/active_record/explain_registry.rb +4 -2
- data/lib/active_record/explain_subscriber.rb +10 -5
- data/lib/active_record/fixture_set/file.rb +35 -9
- data/lib/active_record/fixtures.rb +188 -132
- data/lib/active_record/gem_version.rb +4 -2
- data/lib/active_record/inheritance.rb +148 -112
- data/lib/active_record/integration.rb +70 -28
- data/lib/active_record/internal_metadata.rb +45 -0
- data/lib/active_record/legacy_yaml_adapter.rb +21 -3
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +88 -96
- data/lib/active_record/locking/pessimistic.rb +15 -3
- data/lib/active_record/log_subscriber.rb +95 -33
- data/lib/active_record/migration/command_recorder.rb +133 -90
- data/lib/active_record/migration/compatibility.rb +217 -0
- data/lib/active_record/migration/join_table.rb +8 -6
- data/lib/active_record/migration.rb +581 -282
- data/lib/active_record/model_schema.rb +290 -111
- data/lib/active_record/nested_attributes.rb +264 -222
- data/lib/active_record/no_touching.rb +7 -1
- data/lib/active_record/null_relation.rb +24 -37
- data/lib/active_record/persistence.rb +347 -119
- data/lib/active_record/query_cache.rb +13 -24
- data/lib/active_record/querying.rb +19 -17
- data/lib/active_record/railtie.rb +94 -32
- data/lib/active_record/railties/console_sandbox.rb +2 -0
- data/lib/active_record/railties/controller_runtime.rb +9 -3
- data/lib/active_record/railties/databases.rake +149 -156
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +414 -267
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/batches.rb +204 -55
- data/lib/active_record/relation/calculations.rb +256 -248
- data/lib/active_record/relation/delegation.rb +67 -60
- data/lib/active_record/relation/finder_methods.rb +288 -239
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +86 -86
- data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -24
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
- data/lib/active_record/relation/predicate_builder.rb +116 -119
- data/lib/active_record/relation/query_attribute.rb +45 -0
- data/lib/active_record/relation/query_methods.rb +448 -393
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +11 -13
- data/lib/active_record/relation/where_clause.rb +186 -0
- data/lib/active_record/relation/where_clause_factory.rb +34 -0
- data/lib/active_record/relation.rb +287 -340
- data/lib/active_record/result.rb +54 -36
- data/lib/active_record/runtime_registry.rb +6 -4
- data/lib/active_record/sanitization.rb +155 -124
- data/lib/active_record/schema.rb +30 -24
- data/lib/active_record/schema_dumper.rb +91 -87
- data/lib/active_record/schema_migration.rb +19 -16
- data/lib/active_record/scoping/default.rb +102 -85
- data/lib/active_record/scoping/named.rb +81 -32
- data/lib/active_record/scoping.rb +45 -26
- data/lib/active_record/secure_token.rb +40 -0
- data/lib/active_record/serialization.rb +5 -5
- data/lib/active_record/statement_cache.rb +45 -35
- data/lib/active_record/store.rb +42 -36
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +82 -0
- data/lib/active_record/tasks/database_tasks.rb +134 -96
- data/lib/active_record/tasks/mysql_database_tasks.rb +56 -100
- data/lib/active_record/tasks/postgresql_database_tasks.rb +83 -41
- data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -16
- data/lib/active_record/timestamp.rb +70 -38
- data/lib/active_record/touch_later.rb +64 -0
- data/lib/active_record/transactions.rb +199 -124
- data/lib/active_record/translation.rb +2 -0
- data/lib/active_record/type/adapter_specific_registry.rb +136 -0
- data/lib/active_record/type/date.rb +4 -45
- data/lib/active_record/type/date_time.rb +4 -49
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
- data/lib/active_record/type/internal/timezone.rb +17 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +24 -15
- data/lib/active_record/type/text.rb +2 -2
- data/lib/active_record/type/time.rb +11 -16
- data/lib/active_record/type/type_map.rb +15 -17
- data/lib/active_record/type/unsigned_integer.rb +9 -7
- data/lib/active_record/type.rb +79 -23
- data/lib/active_record/type_caster/connection.rb +33 -0
- data/lib/active_record/type_caster/map.rb +23 -0
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +13 -4
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/presence.rb +14 -13
- data/lib/active_record/validations/uniqueness.rb +40 -41
- data/lib/active_record/validations.rb +38 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +34 -22
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -35
- data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +8 -3
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -1
- data/lib/rails/generators/active_record/migration.rb +18 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
- data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +3 -0
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- data/lib/rails/generators/active_record.rb +7 -5
- metadata +72 -50
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -24
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -23
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -21
- data/lib/active_record/attribute.rb +0 -163
- data/lib/active_record/attribute_set/builder.rb +0 -106
- data/lib/active_record/attribute_set.rb +0 -81
- 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/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- 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/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/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -110
@@ -0,0 +1,140 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module MySQL
|
6
|
+
module DatabaseStatements
|
7
|
+
# Returns an ActiveRecord::Result instance.
|
8
|
+
def select_all(*) # :nodoc:
|
9
|
+
result = if ExplainRegistry.collect? && prepared_statements
|
10
|
+
unprepared_statement { super }
|
11
|
+
else
|
12
|
+
super
|
13
|
+
end
|
14
|
+
discard_remaining_results
|
15
|
+
result
|
16
|
+
end
|
17
|
+
|
18
|
+
def query(sql, name = nil) # :nodoc:
|
19
|
+
execute(sql, name).to_a
|
20
|
+
end
|
21
|
+
|
22
|
+
# Executes the SQL statement in the context of this connection.
|
23
|
+
def execute(sql, name = nil)
|
24
|
+
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
|
25
|
+
# made since we established the connection
|
26
|
+
@connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
|
27
|
+
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
31
|
+
def exec_query(sql, name = "SQL", binds = [], prepare: false)
|
32
|
+
if without_prepared_statement?(binds)
|
33
|
+
execute_and_free(sql, name) do |result|
|
34
|
+
ActiveRecord::Result.new(result.fields, result.to_a) if result
|
35
|
+
end
|
36
|
+
else
|
37
|
+
exec_stmt_and_free(sql, name, binds, cache_stmt: prepare) do |_, result|
|
38
|
+
ActiveRecord::Result.new(result.fields, result.to_a) if result
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def exec_delete(sql, name = nil, binds = [])
|
44
|
+
if without_prepared_statement?(binds)
|
45
|
+
execute_and_free(sql, name) { @connection.affected_rows }
|
46
|
+
else
|
47
|
+
exec_stmt_and_free(sql, name, binds) { |stmt| stmt.affected_rows }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
alias :exec_update :exec_delete
|
51
|
+
|
52
|
+
private
|
53
|
+
def default_insert_value(column)
|
54
|
+
Arel.sql("DEFAULT") unless column.auto_increment?
|
55
|
+
end
|
56
|
+
|
57
|
+
def last_inserted_id(result)
|
58
|
+
@connection.last_id
|
59
|
+
end
|
60
|
+
|
61
|
+
def discard_remaining_results
|
62
|
+
@connection.next_result while @connection.more_results?
|
63
|
+
end
|
64
|
+
|
65
|
+
def supports_set_server_option?
|
66
|
+
@connection.respond_to?(:set_server_option)
|
67
|
+
end
|
68
|
+
|
69
|
+
def multi_statements_enabled?(flags)
|
70
|
+
if flags.is_a?(Array)
|
71
|
+
flags.include?("MULTI_STATEMENTS")
|
72
|
+
else
|
73
|
+
(flags & Mysql2::Client::MULTI_STATEMENTS) != 0
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def with_multi_statements
|
78
|
+
previous_flags = @config[:flags]
|
79
|
+
|
80
|
+
unless multi_statements_enabled?(previous_flags)
|
81
|
+
if supports_set_server_option?
|
82
|
+
@connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
|
83
|
+
else
|
84
|
+
@config[:flags] = Mysql2::Client::MULTI_STATEMENTS
|
85
|
+
reconnect!
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
yield
|
90
|
+
ensure
|
91
|
+
unless multi_statements_enabled?(previous_flags)
|
92
|
+
if supports_set_server_option?
|
93
|
+
@connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
|
94
|
+
else
|
95
|
+
@config[:flags] = previous_flags
|
96
|
+
reconnect!
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def exec_stmt_and_free(sql, name, binds, cache_stmt: false)
|
102
|
+
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
|
103
|
+
# made since we established the connection
|
104
|
+
@connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
|
105
|
+
|
106
|
+
type_casted_binds = type_casted_binds(binds)
|
107
|
+
|
108
|
+
log(sql, name, binds, type_casted_binds) do
|
109
|
+
if cache_stmt
|
110
|
+
cache = @statements[sql] ||= {
|
111
|
+
stmt: @connection.prepare(sql)
|
112
|
+
}
|
113
|
+
stmt = cache[:stmt]
|
114
|
+
else
|
115
|
+
stmt = @connection.prepare(sql)
|
116
|
+
end
|
117
|
+
|
118
|
+
begin
|
119
|
+
result = ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
120
|
+
stmt.execute(*type_casted_binds)
|
121
|
+
end
|
122
|
+
rescue Mysql2::Error => e
|
123
|
+
if cache_stmt
|
124
|
+
@statements.delete(sql)
|
125
|
+
else
|
126
|
+
stmt.close
|
127
|
+
end
|
128
|
+
raise e
|
129
|
+
end
|
130
|
+
|
131
|
+
ret = yield stmt, result
|
132
|
+
result.free if result
|
133
|
+
stmt.close unless cache_stmt
|
134
|
+
ret
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module MySQL
|
6
|
+
class ExplainPrettyPrinter # :nodoc:
|
7
|
+
# Pretty prints the result of an EXPLAIN in a way that resembles the output of the
|
8
|
+
# MySQL shell:
|
9
|
+
#
|
10
|
+
# +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
|
11
|
+
# | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
|
12
|
+
# +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
|
13
|
+
# | 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | |
|
14
|
+
# | 1 | SIMPLE | posts | ALL | NULL | NULL | NULL | NULL | 1 | Using where |
|
15
|
+
# +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
|
16
|
+
# 2 rows in set (0.00 sec)
|
17
|
+
#
|
18
|
+
# This is an exercise in Ruby hyperrealism :).
|
19
|
+
def pp(result, elapsed)
|
20
|
+
widths = compute_column_widths(result)
|
21
|
+
separator = build_separator(widths)
|
22
|
+
|
23
|
+
pp = []
|
24
|
+
|
25
|
+
pp << separator
|
26
|
+
pp << build_cells(result.columns, widths)
|
27
|
+
pp << separator
|
28
|
+
|
29
|
+
result.rows.each do |row|
|
30
|
+
pp << build_cells(row, widths)
|
31
|
+
end
|
32
|
+
|
33
|
+
pp << separator
|
34
|
+
pp << build_footer(result.rows.length, elapsed)
|
35
|
+
|
36
|
+
pp.join("\n") + "\n"
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def compute_column_widths(result)
|
42
|
+
[].tap do |widths|
|
43
|
+
result.columns.each_with_index do |column, i|
|
44
|
+
cells_in_column = [column] + result.rows.map { |r| r[i].nil? ? "NULL" : r[i].to_s }
|
45
|
+
widths << cells_in_column.map(&:length).max
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def build_separator(widths)
|
51
|
+
padding = 1
|
52
|
+
"+" + widths.map { |w| "-" * (w + (padding * 2)) }.join("+") + "+"
|
53
|
+
end
|
54
|
+
|
55
|
+
def build_cells(items, widths)
|
56
|
+
cells = []
|
57
|
+
items.each_with_index do |item, i|
|
58
|
+
item = "NULL" if item.nil?
|
59
|
+
justifier = item.is_a?(Numeric) ? "rjust" : "ljust"
|
60
|
+
cells << item.to_s.send(justifier, widths[i])
|
61
|
+
end
|
62
|
+
"| " + cells.join(" | ") + " |"
|
63
|
+
end
|
64
|
+
|
65
|
+
def build_footer(nrows, elapsed)
|
66
|
+
rows_label = nrows == 1 ? "row" : "rows"
|
67
|
+
"#{nrows} #{rows_label} in set (%.2f sec)" % elapsed
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module MySQL
|
6
|
+
module Quoting # :nodoc:
|
7
|
+
def quote_column_name(name)
|
8
|
+
@quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`".freeze
|
9
|
+
end
|
10
|
+
|
11
|
+
def quote_table_name(name)
|
12
|
+
@quoted_table_names[name] ||= super.gsub(".", "`.`").freeze
|
13
|
+
end
|
14
|
+
|
15
|
+
def unquoted_true
|
16
|
+
1
|
17
|
+
end
|
18
|
+
|
19
|
+
def unquoted_false
|
20
|
+
0
|
21
|
+
end
|
22
|
+
|
23
|
+
def quoted_date(value)
|
24
|
+
if supports_datetime_with_precision?
|
25
|
+
super
|
26
|
+
else
|
27
|
+
super.sub(/\.\d{6}\z/, "")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def quoted_binary(value)
|
32
|
+
"x'#{value.hex}'"
|
33
|
+
end
|
34
|
+
|
35
|
+
def _type_cast(value)
|
36
|
+
case value
|
37
|
+
when Date, Time then value
|
38
|
+
else super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module MySQL
|
6
|
+
class SchemaCreation < AbstractAdapter::SchemaCreation # :nodoc:
|
7
|
+
delegate :add_sql_comment!, :mariadb?, to: :@conn
|
8
|
+
private :add_sql_comment!, :mariadb?
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def visit_DropForeignKey(name)
|
13
|
+
"DROP FOREIGN KEY #{name}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def visit_AddColumnDefinition(o)
|
17
|
+
add_column_position!(super, column_options(o.column))
|
18
|
+
end
|
19
|
+
|
20
|
+
def visit_ChangeColumnDefinition(o)
|
21
|
+
change_column_sql = "CHANGE #{quote_column_name(o.name)} #{accept(o.column)}".dup
|
22
|
+
add_column_position!(change_column_sql, column_options(o.column))
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_table_options!(create_sql, options)
|
26
|
+
add_sql_comment!(super, options[:comment])
|
27
|
+
end
|
28
|
+
|
29
|
+
def add_column_options!(sql, options)
|
30
|
+
# By default, TIMESTAMP columns are NOT NULL, cannot contain NULL values,
|
31
|
+
# and assigning NULL assigns the current timestamp. To permit a TIMESTAMP
|
32
|
+
# column to contain NULL, explicitly declare it with the NULL attribute.
|
33
|
+
# See https://dev.mysql.com/doc/refman/5.7/en/timestamp-initialization.html
|
34
|
+
if /\Atimestamp\b/.match?(options[:column].sql_type) && !options[:primary_key]
|
35
|
+
sql << " NULL" unless options[:null] == false || options_include_default?(options)
|
36
|
+
end
|
37
|
+
|
38
|
+
if charset = options[:charset]
|
39
|
+
sql << " CHARACTER SET #{charset}"
|
40
|
+
end
|
41
|
+
|
42
|
+
if collation = options[:collation]
|
43
|
+
sql << " COLLATE #{collation}"
|
44
|
+
end
|
45
|
+
|
46
|
+
if as = options[:as]
|
47
|
+
sql << " AS (#{as})"
|
48
|
+
if options[:stored]
|
49
|
+
sql << (mariadb? ? " PERSISTENT" : " STORED")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
add_sql_comment!(super, options[:comment])
|
54
|
+
end
|
55
|
+
|
56
|
+
def add_column_position!(sql, options)
|
57
|
+
if options[:first]
|
58
|
+
sql << " FIRST"
|
59
|
+
elsif options[:after]
|
60
|
+
sql << " AFTER #{quote_column_name(options[:after])}"
|
61
|
+
end
|
62
|
+
|
63
|
+
sql
|
64
|
+
end
|
65
|
+
|
66
|
+
def index_in_create(table_name, column_name, options)
|
67
|
+
index_name, index_type, index_columns, _, _, index_using, comment = @conn.add_index_options(table_name, column_name, options)
|
68
|
+
add_sql_comment!("#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})".dup, comment)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module MySQL
|
6
|
+
module ColumnMethods
|
7
|
+
def blob(*args, **options)
|
8
|
+
args.each { |name| column(name, :blob, options) }
|
9
|
+
end
|
10
|
+
|
11
|
+
def tinyblob(*args, **options)
|
12
|
+
args.each { |name| column(name, :tinyblob, options) }
|
13
|
+
end
|
14
|
+
|
15
|
+
def mediumblob(*args, **options)
|
16
|
+
args.each { |name| column(name, :mediumblob, options) }
|
17
|
+
end
|
18
|
+
|
19
|
+
def longblob(*args, **options)
|
20
|
+
args.each { |name| column(name, :longblob, options) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def tinytext(*args, **options)
|
24
|
+
args.each { |name| column(name, :tinytext, options) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def mediumtext(*args, **options)
|
28
|
+
args.each { |name| column(name, :mediumtext, options) }
|
29
|
+
end
|
30
|
+
|
31
|
+
def longtext(*args, **options)
|
32
|
+
args.each { |name| column(name, :longtext, options) }
|
33
|
+
end
|
34
|
+
|
35
|
+
def unsigned_integer(*args, **options)
|
36
|
+
args.each { |name| column(name, :unsigned_integer, options) }
|
37
|
+
end
|
38
|
+
|
39
|
+
def unsigned_bigint(*args, **options)
|
40
|
+
args.each { |name| column(name, :unsigned_bigint, options) }
|
41
|
+
end
|
42
|
+
|
43
|
+
def unsigned_float(*args, **options)
|
44
|
+
args.each { |name| column(name, :unsigned_float, options) }
|
45
|
+
end
|
46
|
+
|
47
|
+
def unsigned_decimal(*args, **options)
|
48
|
+
args.each { |name| column(name, :unsigned_decimal, options) }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
|
53
|
+
include ColumnMethods
|
54
|
+
|
55
|
+
def new_column_definition(name, type, **options) # :nodoc:
|
56
|
+
case type
|
57
|
+
when :virtual
|
58
|
+
type = options[:type]
|
59
|
+
when :primary_key
|
60
|
+
type = :integer
|
61
|
+
options[:limit] ||= 8
|
62
|
+
options[:primary_key] = true
|
63
|
+
when /\Aunsigned_(?<type>.+)\z/
|
64
|
+
type = $~[:type].to_sym
|
65
|
+
options[:unsigned] = true
|
66
|
+
end
|
67
|
+
|
68
|
+
super
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
def aliased_types(name, fallback)
|
73
|
+
fallback
|
74
|
+
end
|
75
|
+
|
76
|
+
def integer_like_primary_key_type(type, options)
|
77
|
+
options[:auto_increment] = true
|
78
|
+
type
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class Table < ActiveRecord::ConnectionAdapters::Table
|
83
|
+
include ColumnMethods
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module MySQL
|
6
|
+
class SchemaDumper < ConnectionAdapters::SchemaDumper # :nodoc:
|
7
|
+
private
|
8
|
+
def prepare_column_options(column)
|
9
|
+
spec = super
|
10
|
+
spec[:unsigned] = "true" if column.unsigned?
|
11
|
+
spec[:auto_increment] = "true" if column.auto_increment?
|
12
|
+
|
13
|
+
if @connection.supports_virtual_columns? && column.virtual?
|
14
|
+
spec[:as] = extract_expression_for_virtual_column(column)
|
15
|
+
spec[:stored] = "true" if /\b(?:STORED|PERSISTENT)\b/.match?(column.extra)
|
16
|
+
spec = { type: schema_type(column).inspect }.merge!(spec)
|
17
|
+
end
|
18
|
+
|
19
|
+
spec
|
20
|
+
end
|
21
|
+
|
22
|
+
def column_spec_for_primary_key(column)
|
23
|
+
spec = super
|
24
|
+
spec.delete(:auto_increment) if column.type == :integer && column.auto_increment?
|
25
|
+
spec
|
26
|
+
end
|
27
|
+
|
28
|
+
def default_primary_key?(column)
|
29
|
+
super && column.auto_increment? && !column.unsigned?
|
30
|
+
end
|
31
|
+
|
32
|
+
def explicit_primary_key_default?(column)
|
33
|
+
column.type == :integer && !column.auto_increment?
|
34
|
+
end
|
35
|
+
|
36
|
+
def schema_type(column)
|
37
|
+
case column.sql_type
|
38
|
+
when /\Atimestamp\b/
|
39
|
+
:timestamp
|
40
|
+
when "tinyblob"
|
41
|
+
:blob
|
42
|
+
else
|
43
|
+
super
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def schema_precision(column)
|
48
|
+
super unless /\A(?:date)?time(?:stamp)?\b/.match?(column.sql_type) && column.precision == 0
|
49
|
+
end
|
50
|
+
|
51
|
+
def schema_collation(column)
|
52
|
+
if column.collation && table_name = column.table_name
|
53
|
+
@table_collation_cache ||= {}
|
54
|
+
@table_collation_cache[table_name] ||=
|
55
|
+
@connection.exec_query("SHOW TABLE STATUS LIKE #{@connection.quote(table_name)}", "SCHEMA").first["Collation"]
|
56
|
+
column.collation.inspect if column.collation != @table_collation_cache[table_name]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def extract_expression_for_virtual_column(column)
|
61
|
+
if @connection.mariadb? && @connection.version < "10.2.5"
|
62
|
+
create_table_info = @connection.send(:create_table_info, column.table_name)
|
63
|
+
column_name = @connection.quote_column_name(column.name)
|
64
|
+
if %r/#{column_name} #{Regexp.quote(column.sql_type)}(?: COLLATE \w+)? AS \((?<expression>.+?)\) #{column.extra}/ =~ create_table_info
|
65
|
+
$~[:expression].inspect
|
66
|
+
end
|
67
|
+
else
|
68
|
+
scope = @connection.send(:quoted_scope, column.table_name)
|
69
|
+
column_name = @connection.quote(column.name)
|
70
|
+
sql = "SELECT generation_expression FROM information_schema.columns" \
|
71
|
+
" WHERE table_schema = #{scope[:schema]}" \
|
72
|
+
" AND table_name = #{scope[:name]}" \
|
73
|
+
" AND column_name = #{column_name}"
|
74
|
+
@connection.query_value(sql, "SCHEMA").inspect
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module MySQL
|
6
|
+
module SchemaStatements # :nodoc:
|
7
|
+
# Returns an array of indexes for the given table.
|
8
|
+
def indexes(table_name)
|
9
|
+
indexes = []
|
10
|
+
current_index = nil
|
11
|
+
execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result|
|
12
|
+
each_hash(result) do |row|
|
13
|
+
if current_index != row[:Key_name]
|
14
|
+
next if row[:Key_name] == "PRIMARY" # skip the primary key
|
15
|
+
current_index = row[:Key_name]
|
16
|
+
|
17
|
+
mysql_index_type = row[:Index_type].downcase.to_sym
|
18
|
+
case mysql_index_type
|
19
|
+
when :fulltext, :spatial
|
20
|
+
index_type = mysql_index_type
|
21
|
+
when :btree, :hash
|
22
|
+
index_using = mysql_index_type
|
23
|
+
end
|
24
|
+
|
25
|
+
indexes << [
|
26
|
+
row[:Table],
|
27
|
+
row[:Key_name],
|
28
|
+
row[:Non_unique].to_i == 0,
|
29
|
+
[],
|
30
|
+
lengths: {},
|
31
|
+
orders: {},
|
32
|
+
type: index_type,
|
33
|
+
using: index_using,
|
34
|
+
comment: row[:Index_comment].presence
|
35
|
+
]
|
36
|
+
end
|
37
|
+
|
38
|
+
indexes.last[-2] << row[:Column_name]
|
39
|
+
indexes.last[-1][:lengths].merge!(row[:Column_name] => row[:Sub_part].to_i) if row[:Sub_part]
|
40
|
+
indexes.last[-1][:orders].merge!(row[:Column_name] => :desc) if row[:Collation] == "D"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
indexes.map { |index| IndexDefinition.new(*index) }
|
45
|
+
end
|
46
|
+
|
47
|
+
def remove_column(table_name, column_name, type = nil, options = {})
|
48
|
+
if foreign_key_exists?(table_name, column: column_name)
|
49
|
+
remove_foreign_key(table_name, column: column_name)
|
50
|
+
end
|
51
|
+
super
|
52
|
+
end
|
53
|
+
|
54
|
+
def internal_string_options_for_primary_key
|
55
|
+
super.tap do |options|
|
56
|
+
if CHARSETS_OF_4BYTES_MAXLEN.include?(charset) && (mariadb? || version < "8.0.0")
|
57
|
+
options[:collation] = collation.sub(/\A[^_]+/, "utf8")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def update_table_definition(table_name, base)
|
63
|
+
MySQL::Table.new(table_name, base)
|
64
|
+
end
|
65
|
+
|
66
|
+
def create_schema_dumper(options)
|
67
|
+
MySQL::SchemaDumper.create(self, options)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
CHARSETS_OF_4BYTES_MAXLEN = ["utf8mb4", "utf16", "utf16le", "utf32"]
|
72
|
+
|
73
|
+
def schema_creation
|
74
|
+
MySQL::SchemaCreation.new(self)
|
75
|
+
end
|
76
|
+
|
77
|
+
def create_table_definition(*args)
|
78
|
+
MySQL::TableDefinition.new(*args)
|
79
|
+
end
|
80
|
+
|
81
|
+
def new_column_from_field(table_name, field)
|
82
|
+
type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
|
83
|
+
if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\([0-6]?\))?\z/i.match?(field[:Default])
|
84
|
+
default, default_function = nil, field[:Default]
|
85
|
+
else
|
86
|
+
default, default_function = field[:Default], nil
|
87
|
+
end
|
88
|
+
|
89
|
+
MySQL::Column.new(
|
90
|
+
field[:Field],
|
91
|
+
default,
|
92
|
+
type_metadata,
|
93
|
+
field[:Null] == "YES",
|
94
|
+
table_name,
|
95
|
+
default_function,
|
96
|
+
field[:Collation],
|
97
|
+
comment: field[:Comment].presence
|
98
|
+
)
|
99
|
+
end
|
100
|
+
|
101
|
+
def fetch_type_metadata(sql_type, extra = "")
|
102
|
+
MySQL::TypeMetadata.new(super(sql_type), extra: extra)
|
103
|
+
end
|
104
|
+
|
105
|
+
def extract_foreign_key_action(specifier)
|
106
|
+
super unless specifier == "RESTRICT"
|
107
|
+
end
|
108
|
+
|
109
|
+
def add_index_length(quoted_columns, **options)
|
110
|
+
lengths = options_for_index_columns(options[:length])
|
111
|
+
quoted_columns.each do |name, column|
|
112
|
+
column << "(#{lengths[name]})" if lengths[name].present?
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def add_options_for_index_columns(quoted_columns, **options)
|
117
|
+
quoted_columns = add_index_length(quoted_columns, options)
|
118
|
+
super
|
119
|
+
end
|
120
|
+
|
121
|
+
def data_source_sql(name = nil, type: nil)
|
122
|
+
scope = quoted_scope(name, type: type)
|
123
|
+
|
124
|
+
sql = "SELECT table_name FROM information_schema.tables".dup
|
125
|
+
sql << " WHERE table_schema = #{scope[:schema]}"
|
126
|
+
sql << " AND table_name = #{scope[:name]}" if scope[:name]
|
127
|
+
sql << " AND table_type = #{scope[:type]}" if scope[:type]
|
128
|
+
sql
|
129
|
+
end
|
130
|
+
|
131
|
+
def quoted_scope(name = nil, type: nil)
|
132
|
+
schema, name = extract_schema_qualified_name(name)
|
133
|
+
scope = {}
|
134
|
+
scope[:schema] = schema ? quote(schema) : "database()"
|
135
|
+
scope[:name] = quote(name) if name
|
136
|
+
scope[:type] = quote(type) if type
|
137
|
+
scope
|
138
|
+
end
|
139
|
+
|
140
|
+
def extract_schema_qualified_name(string)
|
141
|
+
schema, name = string.to_s.scan(/[^`.\s]+|`[^`]*`/)
|
142
|
+
schema, name = nil, schema unless name
|
143
|
+
[schema, name]
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module MySQL
|
6
|
+
class TypeMetadata < DelegateClass(SqlTypeMetadata) # :nodoc:
|
7
|
+
undef to_yaml if method_defined?(:to_yaml)
|
8
|
+
|
9
|
+
attr_reader :extra
|
10
|
+
|
11
|
+
def initialize(type_metadata, extra: "")
|
12
|
+
super(type_metadata)
|
13
|
+
@type_metadata = type_metadata
|
14
|
+
@extra = extra
|
15
|
+
end
|
16
|
+
|
17
|
+
def ==(other)
|
18
|
+
other.is_a?(MySQL::TypeMetadata) &&
|
19
|
+
attributes_for_hash == other.attributes_for_hash
|
20
|
+
end
|
21
|
+
alias eql? ==
|
22
|
+
|
23
|
+
def hash
|
24
|
+
attributes_for_hash.hash
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
def attributes_for_hash
|
30
|
+
[self.class, @type_metadata, extra]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|