activerecord 6.1.7 → 7.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +520 -1385
- data/MIT-LICENSE +1 -1
- data/README.rdoc +31 -31
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +17 -14
- data/lib/active_record/association_relation.rb +2 -12
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +60 -21
- data/lib/active_record/associations/association_scope.rb +17 -12
- data/lib/active_record/associations/belongs_to_association.rb +37 -11
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -4
- data/lib/active_record/associations/builder/association.rb +11 -5
- data/lib/active_record/associations/builder/belongs_to.rb +41 -14
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +4 -4
- data/lib/active_record/associations/builder/singular_association.rb +6 -2
- data/lib/active_record/associations/collection_association.rb +46 -36
- data/lib/active_record/associations/collection_proxy.rb +44 -16
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +29 -19
- data/lib/active_record/associations/has_many_through_association.rb +12 -7
- data/lib/active_record/associations/has_one_association.rb +20 -10
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
- data/lib/active_record/associations/join_dependency.rb +23 -15
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +212 -53
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +153 -0
- data/lib/active_record/associations/preloader/through_association.rb +50 -16
- data/lib/active_record/associations/preloader.rb +50 -121
- data/lib/active_record/associations/singular_association.rb +15 -3
- data/lib/active_record/associations/through_association.rb +25 -14
- data/lib/active_record/associations.rb +404 -509
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +2 -14
- data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +73 -22
- data/lib/active_record/attribute_methods/primary_key.rb +47 -27
- data/lib/active_record/attribute_methods/query.rb +31 -19
- data/lib/active_record/attribute_methods/read.rb +14 -11
- data/lib/active_record/attribute_methods/serialization.rb +174 -37
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -9
- data/lib/active_record/attribute_methods/write.rb +12 -15
- data/lib/active_record/attribute_methods.rb +164 -52
- data/lib/active_record/attributes.rb +51 -49
- data/lib/active_record/autosave_association.rb +74 -57
- data/lib/active_record/base.rb +27 -5
- data/lib/active_record/callbacks.rb +18 -34
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -46
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +327 -612
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -60
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +201 -64
- data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -131
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +377 -142
- data/lib/active_record/connection_adapters/abstract/transaction.rb +361 -76
- data/lib/active_record/connection_adapters/abstract_adapter.rb +624 -163
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +345 -166
- data/lib/active_record/connection_adapters/column.rb +13 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -130
- data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -55
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +45 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +107 -68
- data/lib/active_record/connection_adapters/pool_config.rb +26 -16
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +114 -54
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +137 -104
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +173 -3
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +401 -77
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +518 -251
- data/lib/active_record/connection_adapters/schema_cache.rb +326 -102
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +78 -55
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +68 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +66 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +372 -130
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
- data/lib/active_record/connection_adapters.rb +130 -6
- data/lib/active_record/connection_handling.rb +132 -146
- data/lib/active_record/core.rb +276 -251
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -3
- data/lib/active_record/database_configurations/database_config.rb +34 -10
- data/lib/active_record/database_configurations/hash_config.rb +107 -31
- data/lib/active_record/database_configurations/url_config.rb +38 -13
- data/lib/active_record/database_configurations.rb +96 -60
- data/lib/active_record/delegated_type.rb +90 -20
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +4 -2
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +3 -3
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +68 -0
- data/lib/active_record/encryption/configurable.rb +60 -0
- data/lib/active_record/encryption/context.rb +42 -0
- data/lib/active_record/encryption/contexts.rb +76 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +230 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +170 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +53 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +96 -0
- data/lib/active_record/encryption/null_encryptor.rb +25 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
- data/lib/active_record/encryption/scheme.rb +100 -0
- data/lib/active_record/encryption.rb +56 -0
- data/lib/active_record/enum.rb +163 -63
- data/lib/active_record/errors.rb +210 -27
- data/lib/active_record/explain.rb +21 -12
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +70 -14
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +179 -112
- data/lib/active_record/future_result.rb +178 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +85 -31
- data/lib/active_record/insert_all.rb +148 -32
- data/lib/active_record/integration.rb +14 -10
- data/lib/active_record/internal_metadata.rb +123 -23
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +43 -27
- data/lib/active_record/locking/pessimistic.rb +15 -6
- data/lib/active_record/log_subscriber.rb +41 -29
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
- data/lib/active_record/middleware/database_selector.rb +23 -13
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +113 -16
- data/lib/active_record/migration/compatibility.rb +235 -46
- data/lib/active_record/migration/default_strategy.rb +22 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +374 -177
- data/lib/active_record/model_schema.rb +143 -159
- data/lib/active_record/nested_attributes.rb +48 -21
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +282 -283
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +19 -25
- data/lib/active_record/query_logs.rb +189 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +44 -9
- data/lib/active_record/railtie.rb +234 -71
- data/lib/active_record/railties/controller_runtime.rb +25 -11
- data/lib/active_record/railties/databases.rake +189 -256
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +41 -3
- data/lib/active_record/reflection.rb +325 -103
- data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
- data/lib/active_record/relation/batches.rb +198 -63
- data/lib/active_record/relation/calculations.rb +300 -111
- data/lib/active_record/relation/delegation.rb +33 -22
- data/lib/active_record/relation/finder_methods.rb +123 -52
- data/lib/active_record/relation/merger.rb +26 -19
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +29 -22
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +842 -150
- data/lib/active_record/relation/record_fetch_warning.rb +10 -9
- data/lib/active_record/relation/spawn_methods.rb +7 -6
- data/lib/active_record/relation/where_clause.rb +15 -36
- data/lib/active_record/relation.rb +736 -145
- data/lib/active_record/result.rb +67 -54
- data/lib/active_record/runtime_registry.rb +71 -13
- data/lib/active_record/sanitization.rb +84 -34
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +90 -31
- data/lib/active_record/schema_migration.rb +74 -23
- data/lib/active_record/scoping/default.rb +72 -15
- data/lib/active_record/scoping/named.rb +5 -13
- data/lib/active_record/scoping.rb +65 -34
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/serialization.rb +6 -1
- data/lib/active_record/signed_id.rb +30 -9
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +10 -10
- data/lib/active_record/suppressor.rb +13 -15
- data/lib/active_record/table_metadata.rb +7 -3
- data/lib/active_record/tasks/database_tasks.rb +277 -149
- data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
- data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +173 -155
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +32 -19
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +12 -7
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +118 -41
- data/lib/active_record/translation.rb +3 -5
- data/lib/active_record/type/adapter_specific_registry.rb +32 -14
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +9 -7
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +13 -7
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +64 -15
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +444 -32
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/crud.rb +28 -22
- data/lib/arel/delete_manager.rb +18 -4
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/binary.rb +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
- data/lib/arel/nodes/node.rb +115 -5
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +13 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +7 -2
- data/lib/arel/predications.rb +14 -4
- data/lib/arel/select_manager.rb +11 -5
- data/lib/arel/table.rb +9 -6
- data/lib/arel/tree_manager.rb +8 -15
- data/lib/arel/update_manager.rb +20 -5
- data/lib/arel/visitors/dot.rb +81 -90
- data/lib/arel/visitors/mysql.rb +23 -5
- data/lib/arel/visitors/postgresql.rb +1 -22
- data/lib/arel/visitors/to_sql.rb +170 -36
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +23 -4
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +100 -14
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -67
@@ -15,41 +15,25 @@ module ActiveRecord
|
|
15
15
|
!READ_QUERY.match?(sql.b)
|
16
16
|
end
|
17
17
|
|
18
|
-
def explain(arel, binds = [])
|
19
|
-
sql
|
20
|
-
|
18
|
+
def explain(arel, binds = [], _options = [])
|
19
|
+
sql = "EXPLAIN QUERY PLAN " + to_sql(arel, binds)
|
20
|
+
result = internal_exec_query(sql, "EXPLAIN", [])
|
21
|
+
SQLite3::ExplainPrettyPrinter.new.pp(result)
|
21
22
|
end
|
22
23
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
end
|
24
|
+
def internal_exec_query(sql, name = nil, binds = [], prepare: false, async: false, allow_retry: false) # :nodoc:
|
25
|
+
sql = transform_query(sql)
|
26
|
+
check_if_write_query(sql)
|
27
27
|
|
28
|
-
materialize_transactions
|
29
|
-
mark_transaction_written_if_write(sql)
|
30
|
-
|
31
|
-
log(sql, name) do
|
32
|
-
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
33
|
-
@connection.execute(sql)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def exec_query(sql, name = nil, binds = [], prepare: false)
|
39
|
-
if preventing_writes? && write_query?(sql)
|
40
|
-
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
41
|
-
end
|
42
|
-
|
43
|
-
materialize_transactions
|
44
28
|
mark_transaction_written_if_write(sql)
|
45
29
|
|
46
30
|
type_casted_binds = type_casted_binds(binds)
|
47
31
|
|
48
|
-
log(sql, name, binds, type_casted_binds) do
|
49
|
-
|
32
|
+
log(sql, name, binds, type_casted_binds, async: async) do |notification_payload|
|
33
|
+
with_raw_connection do |conn|
|
50
34
|
# Don't cache statements if they are not prepared
|
51
35
|
unless prepare
|
52
|
-
stmt =
|
36
|
+
stmt = conn.prepare(sql)
|
53
37
|
begin
|
54
38
|
cols = stmt.columns
|
55
39
|
unless without_prepared_statement?(binds)
|
@@ -60,76 +44,111 @@ module ActiveRecord
|
|
60
44
|
stmt.close
|
61
45
|
end
|
62
46
|
else
|
63
|
-
stmt = @statements[sql] ||=
|
47
|
+
stmt = @statements[sql] ||= conn.prepare(sql)
|
64
48
|
cols = stmt.columns
|
65
49
|
stmt.reset!
|
66
50
|
stmt.bind_params(type_casted_binds)
|
67
51
|
records = stmt.to_a
|
68
52
|
end
|
53
|
+
verified!
|
69
54
|
|
70
|
-
build_result(columns: cols, rows: records)
|
55
|
+
result = build_result(columns: cols, rows: records)
|
56
|
+
notification_payload[:row_count] = result.length
|
57
|
+
result
|
71
58
|
end
|
72
59
|
end
|
73
60
|
end
|
74
61
|
|
75
|
-
def exec_delete(sql, name = "SQL", binds = [])
|
76
|
-
|
77
|
-
@
|
62
|
+
def exec_delete(sql, name = "SQL", binds = []) # :nodoc:
|
63
|
+
internal_exec_query(sql, name, binds)
|
64
|
+
@raw_connection.changes
|
78
65
|
end
|
79
66
|
alias :exec_update :exec_delete
|
80
67
|
|
81
|
-
def begin_isolated_db_transaction(isolation)
|
68
|
+
def begin_isolated_db_transaction(isolation) # :nodoc:
|
82
69
|
raise TransactionIsolationError, "SQLite3 only supports the `read_uncommitted` transaction isolation level" if isolation != :read_uncommitted
|
83
70
|
raise StandardError, "You need to enable the shared-cache mode in SQLite mode before attempting to change the transaction isolation level" unless shared_cache?
|
84
71
|
|
85
|
-
|
86
|
-
|
87
|
-
|
72
|
+
with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
|
73
|
+
ActiveSupport::IsolatedExecutionState[:active_record_read_uncommitted] = conn.get_first_value("PRAGMA read_uncommitted")
|
74
|
+
conn.read_uncommitted = true
|
75
|
+
begin_db_transaction
|
76
|
+
end
|
88
77
|
end
|
89
78
|
|
90
|
-
def begin_db_transaction
|
91
|
-
log("begin transaction", "TRANSACTION")
|
79
|
+
def begin_db_transaction # :nodoc:
|
80
|
+
log("begin transaction", "TRANSACTION") do
|
81
|
+
with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
|
82
|
+
result = conn.transaction
|
83
|
+
verified!
|
84
|
+
result
|
85
|
+
end
|
86
|
+
end
|
92
87
|
end
|
93
88
|
|
94
|
-
def commit_db_transaction
|
95
|
-
log("commit transaction", "TRANSACTION")
|
89
|
+
def commit_db_transaction # :nodoc:
|
90
|
+
log("commit transaction", "TRANSACTION") do
|
91
|
+
with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
|
92
|
+
conn.commit
|
93
|
+
end
|
94
|
+
end
|
96
95
|
reset_read_uncommitted
|
97
96
|
end
|
98
97
|
|
99
|
-
def exec_rollback_db_transaction
|
100
|
-
log("rollback transaction", "TRANSACTION")
|
98
|
+
def exec_rollback_db_transaction # :nodoc:
|
99
|
+
log("rollback transaction", "TRANSACTION") do
|
100
|
+
with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
|
101
|
+
conn.rollback
|
102
|
+
end
|
103
|
+
end
|
101
104
|
reset_read_uncommitted
|
102
105
|
end
|
103
106
|
|
107
|
+
# https://stackoverflow.com/questions/17574784
|
108
|
+
# https://www.sqlite.org/lang_datefunc.html
|
109
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')", retryable: true).freeze # :nodoc:
|
110
|
+
private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
|
111
|
+
|
112
|
+
def high_precision_current_timestamp
|
113
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP
|
114
|
+
end
|
115
|
+
|
104
116
|
private
|
117
|
+
def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: false)
|
118
|
+
log(sql, name, async: async) do |notification_payload|
|
119
|
+
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
120
|
+
result = conn.execute(sql)
|
121
|
+
verified!
|
122
|
+
notification_payload[:row_count] = result.length
|
123
|
+
result
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
105
128
|
def reset_read_uncommitted
|
106
|
-
read_uncommitted =
|
129
|
+
read_uncommitted = ActiveSupport::IsolatedExecutionState[:active_record_read_uncommitted]
|
107
130
|
return unless read_uncommitted
|
108
131
|
|
109
|
-
@
|
132
|
+
@raw_connection&.read_uncommitted = read_uncommitted
|
110
133
|
end
|
111
134
|
|
112
135
|
def execute_batch(statements, name = nil)
|
136
|
+
statements = statements.map { |sql| transform_query(sql) }
|
113
137
|
sql = combine_multi_statements(statements)
|
114
138
|
|
115
|
-
|
116
|
-
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
117
|
-
end
|
118
|
-
|
119
|
-
materialize_transactions
|
139
|
+
check_if_write_query(sql)
|
120
140
|
mark_transaction_written_if_write(sql)
|
121
141
|
|
122
|
-
log(sql, name) do
|
123
|
-
|
124
|
-
|
142
|
+
log(sql, name) do |notification_payload|
|
143
|
+
with_raw_connection do |conn|
|
144
|
+
result = conn.execute_batch2(sql)
|
145
|
+
verified!
|
146
|
+
notification_payload[:row_count] = result.length
|
147
|
+
result
|
125
148
|
end
|
126
149
|
end
|
127
150
|
end
|
128
151
|
|
129
|
-
def last_inserted_id(result)
|
130
|
-
@connection.last_insert_row_id
|
131
|
-
end
|
132
|
-
|
133
152
|
def build_fixture_statements(fixture_set)
|
134
153
|
fixture_set.flat_map do |table_name, fixtures|
|
135
154
|
next if fixtures.empty?
|
@@ -140,6 +159,10 @@ module ActiveRecord
|
|
140
159
|
def build_truncate_statement(table_name)
|
141
160
|
"DELETE FROM #{quote_table_name(table_name)}"
|
142
161
|
end
|
162
|
+
|
163
|
+
def returning_column_values(result)
|
164
|
+
result.rows.first
|
165
|
+
end
|
143
166
|
end
|
144
167
|
end
|
145
168
|
end
|
@@ -4,20 +4,58 @@ module ActiveRecord
|
|
4
4
|
module ConnectionAdapters
|
5
5
|
module SQLite3
|
6
6
|
module Quoting # :nodoc:
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
|
10
|
+
QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
|
11
|
+
|
12
|
+
module ClassMethods # :nodoc:
|
13
|
+
def column_name_matcher
|
14
|
+
/
|
15
|
+
\A
|
16
|
+
(
|
17
|
+
(?:
|
18
|
+
# "table_name"."column_name" | function(one or no argument)
|
19
|
+
((?:\w+\.|"\w+"\.)?(?:\w+|"\w+") | \w+\((?:|\g<2>)\))
|
20
|
+
)
|
21
|
+
(?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
|
22
|
+
)
|
23
|
+
(?:\s*,\s*\g<1>)*
|
24
|
+
\z
|
25
|
+
/ix
|
26
|
+
end
|
10
27
|
|
11
|
-
|
12
|
-
|
28
|
+
def column_name_with_order_matcher
|
29
|
+
/
|
30
|
+
\A
|
31
|
+
(
|
32
|
+
(?:
|
33
|
+
# "table_name"."column_name" | function(one or no argument)
|
34
|
+
((?:\w+\.|"\w+"\.)?(?:\w+|"\w+") | \w+\((?:|\g<2>)\))
|
35
|
+
)
|
36
|
+
(?:\s+COLLATE\s+(?:\w+|"\w+"))?
|
37
|
+
(?:\s+ASC|\s+DESC)?
|
38
|
+
)
|
39
|
+
(?:\s*,\s*\g<1>)*
|
40
|
+
\z
|
41
|
+
/ix
|
42
|
+
end
|
43
|
+
|
44
|
+
def quote_column_name(name)
|
45
|
+
QUOTED_COLUMN_NAMES[name] ||= %Q("#{name.to_s.gsub('"', '""')}").freeze
|
46
|
+
end
|
47
|
+
|
48
|
+
def quote_table_name(name)
|
49
|
+
QUOTED_TABLE_NAMES[name] ||= %Q("#{name.to_s.gsub('"', '""').gsub(".", "\".\"")}").freeze
|
50
|
+
end
|
13
51
|
end
|
14
52
|
|
15
|
-
def
|
16
|
-
|
53
|
+
def quote_string(s)
|
54
|
+
::SQLite3::Database.quote(s)
|
17
55
|
end
|
18
56
|
|
19
|
-
def
|
20
|
-
|
57
|
+
def quote_table_name_for_assignment(table, attr)
|
58
|
+
quote_column_name(attr)
|
21
59
|
end
|
22
60
|
|
23
61
|
def quoted_time(value)
|
@@ -45,57 +83,33 @@ module ActiveRecord
|
|
45
83
|
0
|
46
84
|
end
|
47
85
|
|
48
|
-
def
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
86
|
+
def quote_default_expression(value, column) # :nodoc:
|
87
|
+
if value.is_a?(Proc)
|
88
|
+
value = value.call
|
89
|
+
if value.match?(/\A\w+\(.*\)\z/)
|
90
|
+
"(#{value})"
|
91
|
+
else
|
92
|
+
value
|
93
|
+
end
|
94
|
+
else
|
95
|
+
super
|
96
|
+
end
|
54
97
|
end
|
55
98
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
(?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
|
64
|
-
)
|
65
|
-
(?:\s*,\s*\g<1>)*
|
66
|
-
\z
|
67
|
-
/ix
|
68
|
-
|
69
|
-
COLUMN_NAME_WITH_ORDER = /
|
70
|
-
\A
|
71
|
-
(
|
72
|
-
(?:
|
73
|
-
# "table_name"."column_name" | function(one or no argument)
|
74
|
-
((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")) | \w+\((?:|\g<2>)\)
|
75
|
-
)
|
76
|
-
(?:\s+ASC|\s+DESC)?
|
77
|
-
)
|
78
|
-
(?:\s*,\s*\g<1>)*
|
79
|
-
\z
|
80
|
-
/ix
|
81
|
-
|
82
|
-
private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
|
83
|
-
|
84
|
-
private
|
85
|
-
def _type_cast(value)
|
86
|
-
case value
|
87
|
-
when BigDecimal
|
88
|
-
value.to_f
|
89
|
-
when String
|
90
|
-
if value.encoding == Encoding::ASCII_8BIT
|
91
|
-
super(value.encode(Encoding::UTF_8))
|
92
|
-
else
|
93
|
-
super
|
94
|
-
end
|
99
|
+
def type_cast(value) # :nodoc:
|
100
|
+
case value
|
101
|
+
when BigDecimal, Rational
|
102
|
+
value.to_f
|
103
|
+
when String
|
104
|
+
if value.encoding == Encoding::ASCII_8BIT
|
105
|
+
super(value.encode(Encoding::UTF_8))
|
95
106
|
else
|
96
107
|
super
|
97
108
|
end
|
109
|
+
else
|
110
|
+
super
|
98
111
|
end
|
112
|
+
end
|
99
113
|
end
|
100
114
|
end
|
101
115
|
end
|
@@ -5,6 +5,18 @@ module ActiveRecord
|
|
5
5
|
module SQLite3
|
6
6
|
class SchemaCreation < SchemaCreation # :nodoc:
|
7
7
|
private
|
8
|
+
def visit_AddForeignKey(o)
|
9
|
+
super.dup.tap do |sql|
|
10
|
+
sql << " DEFERRABLE INITIALLY #{o.options[:deferrable].to_s.upcase}" if o.deferrable
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def visit_ForeignKeyDefinition(o)
|
15
|
+
super.dup.tap do |sql|
|
16
|
+
sql << " DEFERRABLE INITIALLY #{o.deferrable.to_s.upcase}" if o.deferrable
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
8
20
|
def supports_index_using?
|
9
21
|
false
|
10
22
|
end
|
@@ -13,6 +25,16 @@ module ActiveRecord
|
|
13
25
|
if options[:collation]
|
14
26
|
sql << " COLLATE \"#{options[:collation]}\""
|
15
27
|
end
|
28
|
+
|
29
|
+
if as = options[:as]
|
30
|
+
sql << " GENERATED ALWAYS AS (#{as})"
|
31
|
+
|
32
|
+
if options[:stored]
|
33
|
+
sql << " STORED"
|
34
|
+
else
|
35
|
+
sql << " VIRTUAL"
|
36
|
+
end
|
37
|
+
end
|
16
38
|
super
|
17
39
|
end
|
18
40
|
end
|
@@ -3,16 +3,36 @@
|
|
3
3
|
module ActiveRecord
|
4
4
|
module ConnectionAdapters
|
5
5
|
module SQLite3
|
6
|
+
# = Active Record SQLite3 Adapter \Table Definition
|
6
7
|
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
|
8
|
+
def change_column(column_name, type, **options)
|
9
|
+
name = column_name.to_s
|
10
|
+
@columns_hash[name] = nil
|
11
|
+
column(name, type, **options)
|
12
|
+
end
|
13
|
+
|
7
14
|
def references(*args, **options)
|
8
15
|
super(*args, type: :integer, **options)
|
9
16
|
end
|
10
17
|
alias :belongs_to :references
|
11
18
|
|
19
|
+
def new_column_definition(name, type, **options) # :nodoc:
|
20
|
+
case type
|
21
|
+
when :virtual
|
22
|
+
type = options[:type]
|
23
|
+
end
|
24
|
+
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
12
28
|
private
|
13
29
|
def integer_like_primary_key_type(type, options)
|
14
30
|
:primary_key
|
15
31
|
end
|
32
|
+
|
33
|
+
def valid_column_definition_options
|
34
|
+
super + [:as, :type, :stored]
|
35
|
+
end
|
16
36
|
end
|
17
37
|
end
|
18
38
|
end
|
@@ -12,6 +12,22 @@ module ActiveRecord
|
|
12
12
|
def explicit_primary_key_default?(column)
|
13
13
|
column.bigint?
|
14
14
|
end
|
15
|
+
|
16
|
+
def prepare_column_options(column)
|
17
|
+
spec = super
|
18
|
+
|
19
|
+
if @connection.supports_virtual_columns? && column.virtual?
|
20
|
+
spec[:as] = extract_expression_for_virtual_column(column)
|
21
|
+
spec[:stored] = column.virtual_stored?
|
22
|
+
spec = { type: schema_type(column).inspect }.merge!(spec)
|
23
|
+
end
|
24
|
+
|
25
|
+
spec
|
26
|
+
end
|
27
|
+
|
28
|
+
def extract_expression_for_virtual_column(column)
|
29
|
+
column.default_function.inspect
|
30
|
+
end
|
15
31
|
end
|
16
32
|
end
|
17
33
|
end
|
@@ -6,7 +6,7 @@ module ActiveRecord
|
|
6
6
|
module SchemaStatements # :nodoc:
|
7
7
|
# Returns an array of indexes for the given table.
|
8
8
|
def indexes(table_name)
|
9
|
-
|
9
|
+
internal_exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", "SCHEMA").filter_map do |row|
|
10
10
|
# Indexes SQLite creates implicitly for internal use start with "sqlite_".
|
11
11
|
# See https://www.sqlite.org/fileformat2.html#intschema
|
12
12
|
next if row["name"].start_with?("sqlite_")
|
@@ -21,9 +21,9 @@ module ActiveRecord
|
|
21
21
|
WHERE name = #{quote(row['name'])} AND type = 'index'
|
22
22
|
SQL
|
23
23
|
|
24
|
-
/\bON\b\s*"?(\w+?)"?\s*\((?<expressions>.+?)\)(?:\s*WHERE\b\s*(?<where>.+))?\z/i =~ index_sql
|
24
|
+
/\bON\b\s*"?(\w+?)"?\s*\((?<expressions>.+?)\)(?:\s*WHERE\b\s*(?<where>.+))?(?:\s*\/\*.*\*\/)?\z/i =~ index_sql
|
25
25
|
|
26
|
-
columns =
|
26
|
+
columns = internal_exec_query("PRAGMA index_info(#{quote(row['name'])})", "SCHEMA").map do |col|
|
27
27
|
col["name"]
|
28
28
|
end
|
29
29
|
|
@@ -49,10 +49,12 @@ module ActiveRecord
|
|
49
49
|
where: where,
|
50
50
|
orders: orders
|
51
51
|
)
|
52
|
-
end
|
52
|
+
end
|
53
53
|
end
|
54
54
|
|
55
55
|
def add_foreign_key(from_table, to_table, **options)
|
56
|
+
assert_valid_deferrable(options[:deferrable])
|
57
|
+
|
56
58
|
alter_table(from_table) do |definition|
|
57
59
|
to_table = strip_table_name_prefix_and_suffix(to_table)
|
58
60
|
definition.foreign_key(to_table, **options)
|
@@ -60,6 +62,8 @@ module ActiveRecord
|
|
60
62
|
end
|
61
63
|
|
62
64
|
def remove_foreign_key(from_table, to_table = nil, **options)
|
65
|
+
return if options.delete(:if_exists) == true && !foreign_key_exists?(from_table, to_table)
|
66
|
+
|
63
67
|
to_table ||= options[:to_table]
|
64
68
|
options = options.except(:name, :to_table, :validate)
|
65
69
|
foreign_keys = foreign_keys(from_table)
|
@@ -82,11 +86,11 @@ module ActiveRecord
|
|
82
86
|
table_sql = query_value(<<-SQL, "SCHEMA")
|
83
87
|
SELECT sql
|
84
88
|
FROM sqlite_master
|
85
|
-
WHERE name = #{
|
89
|
+
WHERE name = #{quote(table_name)} AND type = 'table'
|
86
90
|
UNION ALL
|
87
91
|
SELECT sql
|
88
92
|
FROM sqlite_temp_master
|
89
|
-
WHERE name = #{
|
93
|
+
WHERE name = #{quote(table_name)} AND type = 'table'
|
90
94
|
SQL
|
91
95
|
|
92
96
|
table_sql.to_s.scan(/CONSTRAINT\s+(?<name>\w+)\s+CHECK\s+\((?<expression>(:?[^()]|\(\g<expression>\))+)\)/i).map do |name, expression|
|
@@ -100,7 +104,9 @@ module ActiveRecord
|
|
100
104
|
end
|
101
105
|
end
|
102
106
|
|
103
|
-
def remove_check_constraint(table_name, expression = nil, **options)
|
107
|
+
def remove_check_constraint(table_name, expression = nil, if_exists: false, **options)
|
108
|
+
return if if_exists && !check_constraint_exists?(table_name, **options)
|
109
|
+
|
104
110
|
check_constraints = check_constraints(table_name)
|
105
111
|
chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name
|
106
112
|
check_constraints.delete_if { |chk| chk.name == chk_name_to_delete }
|
@@ -111,9 +117,13 @@ module ActiveRecord
|
|
111
117
|
SQLite3::SchemaDumper.create(self, options)
|
112
118
|
end
|
113
119
|
|
120
|
+
def schema_creation # :nodoc
|
121
|
+
SQLite3::SchemaCreation.new(self)
|
122
|
+
end
|
123
|
+
|
114
124
|
private
|
115
|
-
def
|
116
|
-
|
125
|
+
def valid_table_definition_options
|
126
|
+
super + [:rename]
|
117
127
|
end
|
118
128
|
|
119
129
|
def create_table_definition(name, **options)
|
@@ -124,21 +134,42 @@ module ActiveRecord
|
|
124
134
|
super unless internal
|
125
135
|
end
|
126
136
|
|
127
|
-
def new_column_from_field(table_name, field)
|
128
|
-
default =
|
129
|
-
case field["dflt_value"]
|
130
|
-
when /^null$/i
|
131
|
-
nil
|
132
|
-
when /^'(.*)'$/m
|
133
|
-
$1.gsub("''", "'")
|
134
|
-
when /^"(.*)"$/m
|
135
|
-
$1.gsub('""', '"')
|
136
|
-
else
|
137
|
-
field["dflt_value"]
|
138
|
-
end
|
137
|
+
def new_column_from_field(table_name, field, definitions)
|
138
|
+
default = field["dflt_value"]
|
139
139
|
|
140
140
|
type_metadata = fetch_type_metadata(field["type"])
|
141
|
-
|
141
|
+
default_value = extract_value_from_default(default)
|
142
|
+
generated_type = extract_generated_type(field)
|
143
|
+
|
144
|
+
if generated_type.present?
|
145
|
+
default_function = default
|
146
|
+
else
|
147
|
+
default_function = extract_default_function(default_value, default)
|
148
|
+
end
|
149
|
+
|
150
|
+
rowid = is_column_the_rowid?(field, definitions)
|
151
|
+
|
152
|
+
Column.new(
|
153
|
+
field["name"],
|
154
|
+
default_value,
|
155
|
+
type_metadata,
|
156
|
+
field["notnull"].to_i == 0,
|
157
|
+
default_function,
|
158
|
+
collation: field["collation"],
|
159
|
+
auto_increment: field["auto_increment"],
|
160
|
+
rowid: rowid,
|
161
|
+
generated_type: generated_type
|
162
|
+
)
|
163
|
+
end
|
164
|
+
|
165
|
+
INTEGER_REGEX = /integer/i
|
166
|
+
# if a rowid table has a primary key that consists of a single column
|
167
|
+
# and the declared type of that column is "INTEGER" in any mixture of upper and lower case,
|
168
|
+
# then the column becomes an alias for the rowid.
|
169
|
+
def is_column_the_rowid?(field, column_definitions)
|
170
|
+
return false unless INTEGER_REGEX.match?(field["type"]) && field["pk"] == 1
|
171
|
+
# is the primary key a single column?
|
172
|
+
column_definitions.one? { |c| c["pk"] > 0 }
|
142
173
|
end
|
143
174
|
|
144
175
|
def data_source_sql(name = nil, type: nil)
|
@@ -164,6 +195,19 @@ module ActiveRecord
|
|
164
195
|
scope[:type] = type if type
|
165
196
|
scope
|
166
197
|
end
|
198
|
+
|
199
|
+
def assert_valid_deferrable(deferrable)
|
200
|
+
return if !deferrable || %i(immediate deferred).include?(deferrable)
|
201
|
+
|
202
|
+
raise ArgumentError, "deferrable must be `:immediate` or `:deferred`, got: `#{deferrable.inspect}`"
|
203
|
+
end
|
204
|
+
|
205
|
+
def extract_generated_type(field)
|
206
|
+
case field["hidden"]
|
207
|
+
when 2 then :virtual
|
208
|
+
when 3 then :stored
|
209
|
+
end
|
210
|
+
end
|
167
211
|
end
|
168
212
|
end
|
169
213
|
end
|