activerecord-cockroachdb-adapter 8.0.3 → 8.1.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 +3 -0
- data/CONTRIBUTING.md +10 -17
- data/Gemfile +2 -1
- data/Rakefile +0 -19
- data/lib/active_record/connection_adapters/cockroachdb/column.rb +66 -24
- data/lib/active_record/connection_adapters/cockroachdb/database_statements.rb +13 -3
- data/lib/active_record/connection_adapters/cockroachdb/database_tasks.rb +1 -1
- data/lib/active_record/connection_adapters/cockroachdb/oid/spatial.rb +34 -19
- data/lib/active_record/connection_adapters/cockroachdb/quoting.rb +19 -3
- data/lib/active_record/connection_adapters/cockroachdb/referential_integrity.rb +87 -55
- data/lib/active_record/connection_adapters/cockroachdb/schema_statements.rb +181 -102
- data/lib/active_record/connection_adapters/cockroachdb_adapter.rb +39 -21
- data/lib/version.rb +1 -1
- data/test/cases/adapter_test.rb +98 -0
- data/test/cases/adapters/cockroachdb/referential_integrity_test.rb +51 -0
- data/test/cases/adapters/postgresql/active_schema_test.rb +71 -0
- data/test/cases/adapters/postgresql/change_schema_test.rb +75 -0
- data/test/cases/adapters/postgresql/connection_test.rb +46 -0
- data/test/cases/adapters/postgresql/ddl_test.rb +326 -0
- data/test/cases/adapters/postgresql/interval_test.rb +131 -0
- data/test/cases/adapters/postgresql/nested_class_test.rb +22 -0
- data/test/cases/adapters/postgresql/numeric_test.rb +28 -0
- data/test/cases/adapters/postgresql/postgis_test.rb +185 -0
- data/test/cases/adapters/postgresql/postgresql_adapter_test.rb +115 -0
- data/test/cases/adapters/postgresql/quoting_test.rb +30 -0
- data/test/cases/adapters/postgresql/schema_statements_test.rb +29 -0
- data/test/cases/adapters/postgresql/serial_test.rb +199 -0
- data/test/cases/adapters/postgresql/spatial_queries_test.rb +118 -0
- data/test/cases/adapters/postgresql/spatial_setup_test.rb +18 -0
- data/test/cases/adapters/postgresql/spatial_type_test.rb +41 -0
- data/test/cases/adapters/postgresql/timestamp_test.rb +58 -0
- data/test/cases/adapters/postgresql/virtual_column_test.rb +39 -0
- data/test/cases/associations/eager_load_nested_include_test.rb +111 -0
- data/test/cases/associations/left_outer_join_association_test.rb +30 -0
- data/test/cases/associations_test.rb +108 -0
- data/test/cases/base_test.rb +31 -0
- data/test/cases/comment_test.rb +75 -0
- data/test/cases/connection_adapters/type_test.rb +28 -0
- data/test/cases/database_configurations/resolver_test.rb +24 -0
- data/test/cases/defaults_test.rb +45 -0
- data/test/cases/dirty_test.rb +24 -0
- data/test/cases/fixtures_test.rb +541 -0
- data/test/cases/helper_cockroachdb.rb +232 -0
- data/test/cases/inheritance_test.rb +42 -0
- data/test/cases/invertible_migration_test.rb +73 -0
- data/test/cases/marshal_serialization_test.rb +45 -0
- data/test/cases/migration/change_schema_test.rb +140 -0
- data/test/cases/migration/check_constraint_test.rb +125 -0
- data/test/cases/migration/columns_test.rb +50 -0
- data/test/cases/migration/create_join_table_test.rb +66 -0
- data/test/cases/migration/foreign_key_test.rb +390 -0
- data/test/cases/migration/hidden_column_test.rb +70 -0
- data/test/cases/migration/references_foreign_key_test.rb +124 -0
- data/test/cases/migration_test.rb +120 -0
- data/test/cases/persistence_test.rb +33 -0
- data/test/cases/primary_keys_test.rb +134 -0
- data/test/cases/relation/aost_test.rb +57 -0
- data/test/cases/relation/or_test.rb +26 -0
- data/test/cases/relation/table_hints_test.rb +124 -0
- data/test/cases/relation_test.rb +26 -0
- data/test/cases/relations_test.rb +29 -0
- data/test/cases/schema_dumper_test.rb +221 -0
- data/test/cases/show_create_test.rb +14 -0
- data/test/cases/strict_loading_test.rb +34 -0
- data/test/cases/tasks/cockroachdb_rake_test.rb +113 -0
- data/test/cases/test_fixtures_test.rb +57 -0
- data/test/cases/transactions_test.rb +51 -0
- data/test/cases/unsafe_raw_sql_test.rb +22 -0
- data/test/config.yml +23 -0
- data/test/excludes/ActiveRecord/AdapterTest.rb +3 -0
- data/test/excludes/ActiveRecord/AdapterTestWithoutTransaction.rb +25 -0
- data/test/excludes/ActiveRecord/ConnectionAdapters/PoolConfig/ResolverTest.rb +1 -0
- data/test/excludes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter/BindParameterTest.rb +15 -0
- data/test/excludes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter/QuotingTest.rb +22 -0
- data/test/excludes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapterPreventWritesLegacyTest.rb +1 -0
- data/test/excludes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapterPreventWritesTest.rb +1 -0
- data/test/excludes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapterTest.rb +40 -0
- data/test/excludes/ActiveRecord/ConnectionAdapters/RegistrationIsolatedTest.rb +14 -0
- data/test/excludes/ActiveRecord/Encryption/EncryptionPerformanceTest.rb +1 -0
- data/test/excludes/ActiveRecord/Encryption/EnvelopeEncryptionPerformanceTest.rb +1 -0
- data/test/excludes/ActiveRecord/Encryption/ExtendedDeterministicQueriesPerformanceTest.rb +1 -0
- data/test/excludes/ActiveRecord/Encryption/StoragePerformanceTest.rb +2 -0
- data/test/excludes/ActiveRecord/InstrumentationTest.rb +16 -0
- data/test/excludes/ActiveRecord/InvertibleMigrationTest.rb +2 -0
- data/test/excludes/ActiveRecord/Migration/ChangeSchemaTest.rb +7 -0
- data/test/excludes/ActiveRecord/Migration/CheckConstraintTest.rb +6 -0
- data/test/excludes/ActiveRecord/Migration/ColumnsTest.rb +4 -0
- data/test/excludes/ActiveRecord/Migration/CompatibilityTest.rb +50 -0
- data/test/excludes/ActiveRecord/Migration/CompositeForeignKeyTest.rb +2 -0
- data/test/excludes/ActiveRecord/Migration/CreateJoinTableTest.rb +2 -0
- data/test/excludes/ActiveRecord/Migration/ForeignKeyInCreateTest.rb +1 -0
- data/test/excludes/ActiveRecord/Migration/ForeignKeyTest.rb +35 -0
- data/test/excludes/ActiveRecord/Migration/InvalidOptionsTest.rb +14 -0
- data/test/excludes/ActiveRecord/Migration/PGChangeSchemaTest.rb +8 -0
- data/test/excludes/ActiveRecord/Migration/ReferencesForeignKeyTest.rb +7 -0
- data/test/excludes/ActiveRecord/Migration/ReferencesIndexTest.rb +7 -0
- data/test/excludes/ActiveRecord/Migration/ReferencesStatementsTest.rb +3 -0
- data/test/excludes/ActiveRecord/MysqlDBCreateWithInvalidPermissionsTest.rb +1 -0
- data/test/excludes/ActiveRecord/OrTest.rb +1 -0
- data/test/excludes/ActiveRecord/PostgreSQLStructureDumpTest.rb +10 -0
- data/test/excludes/ActiveRecord/PostgresqlConnectionTest.rb +12 -0
- data/test/excludes/ActiveRecord/PostgresqlTransactionNestedTest.rb +14 -0
- data/test/excludes/ActiveRecord/PostgresqlTransactionTest.rb +4 -0
- data/test/excludes/ActiveRecord/RelationTest.rb +2 -0
- data/test/excludes/ActiveRecord/TooManyOrTest.rb +1 -0
- data/test/excludes/ActiveSupportSubclassWithFixturesTest.rb +1 -0
- data/test/excludes/AssociationDeprecationTest/NotifyModeTest.rb +2 -0
- data/test/excludes/AssociationDeprecationTest/RaiseBacktraceModeTest.rb +2 -0
- data/test/excludes/AssociationDeprecationTest/RaiseModeTest.rb +2 -0
- data/test/excludes/AssociationDeprecationTest/WarnBacktraceModeTest.rb +2 -0
- data/test/excludes/AssociationDeprecationTest/WarnModeTest.rb +2 -0
- data/test/excludes/AssociationDeprecationTest/fix_backtrace_cleaner.rb +10 -0
- data/test/excludes/BasicsTest.rb +1 -0
- data/test/excludes/BulkAlterTableMigrationsTest.rb +7 -0
- data/test/excludes/CalculationsTest.rb +4 -0
- data/test/excludes/CommentTest.rb +2 -0
- data/test/excludes/CoreTest.rb +1 -0
- data/test/excludes/CreateOrFindByWithinTransactions.rb +3 -0
- data/test/excludes/DefaultsUsingMultipleSchemasAndDomainTest.rb +7 -0
- data/test/excludes/DirtyTest.rb +3 -0
- data/test/excludes/EachTest.rb +8 -0
- data/test/excludes/EagerLoadPolyAssocsTest.rb +1 -0
- data/test/excludes/ExplicitlyNamedIndexMigrationTest.rb +1 -0
- data/test/excludes/FixturesResetPkSequenceTest.rb +3 -0
- data/test/excludes/FixturesTest.rb +21 -0
- data/test/excludes/FixturesWithForeignKeyViolationsTest.rb +2 -0
- data/test/excludes/ForeignTableTest.rb +10 -0
- data/test/excludes/InheritanceComputeTypeTest.rb +1 -0
- data/test/excludes/LeftOuterJoinAssociationTest.rb +2 -0
- data/test/excludes/LegacyPrimaryKeyTest/V4_2.rb +6 -0
- data/test/excludes/LegacyPrimaryKeyTest/V5_0.rb +6 -0
- data/test/excludes/MarshalSerializationTest.rb +4 -0
- data/test/excludes/MaterializedViewTest.rb +13 -0
- data/test/excludes/MigrationTest.rb +2 -0
- data/test/excludes/MultiDbMigratorTest.rb +2 -0
- data/test/excludes/NestedRelationScopingTest.rb +1 -0
- data/test/excludes/OrTest.rb +1 -0
- data/test/excludes/PersistenceTest.rb +3 -0
- data/test/excludes/PessimisticLockingTest.rb +1 -0
- data/test/excludes/PostgreSQLExplainTest.rb +7 -0
- data/test/excludes/PostgreSQLGeometricLineTest.rb +3 -0
- data/test/excludes/PostgreSQLGeometricTypesTest.rb +7 -0
- data/test/excludes/PostgreSQLPartitionsTest.rb +1 -0
- data/test/excludes/PostgreSQLReferentialIntegrityTest.rb +14 -0
- data/test/excludes/PostgresqlArrayTest.rb +21 -0
- data/test/excludes/PostgresqlBigSerialTest.rb +7 -0
- data/test/excludes/PostgresqlBitStringTest.rb +2 -0
- data/test/excludes/PostgresqlByteaTest.rb +1 -0
- data/test/excludes/PostgresqlCitextTest.rb +7 -0
- data/test/excludes/PostgresqlCollationTest.rb +5 -0
- data/test/excludes/PostgresqlCompositeTest.rb +2 -0
- data/test/excludes/PostgresqlCompositeWithCustomOIDTest.rb +2 -0
- data/test/excludes/PostgresqlDataTypeTest.rb +9 -0
- data/test/excludes/PostgresqlDefaultExpressionTest.rb +1 -0
- data/test/excludes/PostgresqlDeferredConstraintsTest.rb +3 -0
- data/test/excludes/PostgresqlDomainTest.rb +2 -0
- data/test/excludes/PostgresqlExtensionMigrationTest.rb +5 -0
- data/test/excludes/PostgresqlFullTextTest.rb +3 -0
- data/test/excludes/PostgresqlGeometricTest.rb +4 -0
- data/test/excludes/PostgresqlHstoreTest.rb +45 -0
- data/test/excludes/PostgresqlInfinityTest.rb +5 -0
- data/test/excludes/PostgresqlIntervalTest.rb +1 -0
- data/test/excludes/PostgresqlJSONBTest.rb +27 -0
- data/test/excludes/PostgresqlJSONTest.rb +27 -0
- data/test/excludes/PostgresqlLtreeTest.rb +4 -0
- data/test/excludes/PostgresqlMoneyTest.rb +12 -0
- data/test/excludes/PostgresqlNetworkTest.rb +69 -0
- data/test/excludes/PostgresqlNumberTest.rb +1 -0
- data/test/excludes/PostgresqlPointTest.rb +15 -0
- data/test/excludes/PostgresqlRangeTest.rb +3 -0
- data/test/excludes/PostgresqlRenameTableTest.rb +2 -0
- data/test/excludes/PostgresqlSerialTest.rb +7 -0
- data/test/excludes/PostgresqlTimestampFixtureTest.rb +2 -0
- data/test/excludes/PostgresqlTimestampMigrationTest.rb +3 -0
- data/test/excludes/PostgresqlTypeLookupTest.rb +2 -0
- data/test/excludes/PostgresqlUUIDGenerationTest.rb +7 -0
- data/test/excludes/PostgresqlUUIDTest.rb +1 -0
- data/test/excludes/PostgresqlVirtualColumnTest.rb +17 -0
- data/test/excludes/PostgresqlXMLTest.rb +5 -0
- data/test/excludes/PrimaryKeyIntegerNilDefaultTest.rb +1 -0
- data/test/excludes/PrimaryKeyIntegerTest.rb +3 -0
- data/test/excludes/PrimaryKeysTest.rb +2 -0
- data/test/excludes/RelationMergingTest.rb +15 -0
- data/test/excludes/RelationTest.rb +3 -0
- data/test/excludes/ReservedWordsMigrationTest.rb +1 -0
- data/test/excludes/SameNameDifferentDatabaseFixturesTest.rb +1 -0
- data/test/excludes/SanitizeTest.rb +13 -0
- data/test/excludes/SchemaAuthorizationTest.rb +8 -0
- data/test/excludes/SchemaCreateTableOptionsTest.rb +2 -0
- data/test/excludes/SchemaDumperDefaultsTest.rb +1 -0
- data/test/excludes/SchemaDumperTest.rb +11 -0
- data/test/excludes/SchemaForeignKeyTest.rb +1 -0
- data/test/excludes/SchemaIndexNullsOrderTest.rb +2 -0
- data/test/excludes/SchemaIndexOpclassTest.rb +3 -0
- data/test/excludes/SchemaTest.rb +49 -0
- data/test/excludes/SequenceNameDetectionTestCases/CollidedSequenceNameTest.rb +1 -0
- data/test/excludes/SequenceNameDetectionTestCases/LongerSequenceNameDetectionTest.rb +1 -0
- data/test/excludes/StrictLoadingFixturesTest.rb +1 -0
- data/test/excludes/TestFixturesTest.rb +1 -0
- data/test/excludes/TransactionInstrumentationTest.rb +1 -0
- data/test/excludes/TransactionIsolationTest.rb +1 -0
- data/test/excludes/TypeTest.rb +1 -0
- data/test/excludes/UniquenessValidationTest.rb +2 -0
- data/test/excludes/UnloggedTablesTest.rb +3 -0
- data/test/excludes/UnsafeRawSqlTest.rb +2 -0
- data/test/excludes/UpdateableViewTest.rb +5 -0
- data/test/excludes/WithAnnotationsTest.rb +6 -0
- data/test/models/building.rb +4 -0
- data/test/models/spatial_model.rb +5 -0
- data/test/schema/cockroachdb_specific_schema.rb +218 -0
- data/test/support/copy_cat.rb +83 -0
- data/test/support/exclude_from_transactional_tests.rb +33 -0
- data/test/support/paths_cockroachdb.rb +46 -0
- data/test/support/rake_helpers.rb +37 -0
- data/test/support/sql_logger.rb +55 -0
- data/test/support/template_creator.rb +114 -0
- metadata +422 -34
- data/.editorconfig +0 -7
- data/.github/issue_template.md +0 -46
- data/.github/reproduction_scripts/migrations.rb +0 -60
- data/.github/reproduction_scripts/models_and_database.rb +0 -49
- data/.github/workflows/ci.yml +0 -96
- data/.github/workflows/docker.yml +0 -57
- data/.gitignore +0 -18
- data/.gitmodules +0 -0
- data/activerecord-cockroachdb-adapter.gemspec +0 -38
- data/bin/console +0 -36
- data/bin/console_schemas/default.rb +0 -11
- data/bin/console_schemas/schemas.rb +0 -23
- data/bin/setup +0 -8
- data/bin/start-cockroachdb +0 -33
- data/build/Dockerfile +0 -14
- data/build/config.teamcity.yml +0 -31
- data/build/local-test.sh +0 -32
- data/build/teamcity-test.sh +0 -76
- data/docker.sh +0 -35
- data/lib/active_record/connection_adapters/cockroachdb/spatial_column_info.rb +0 -60
- data/setup.sql +0 -18
|
@@ -0,0 +1 @@
|
|
|
1
|
+
exclude :test_schema_dump_with_long_table_name, "Serial columns are dumped as a bigserials against CockroachDB. This happens because serial columns are backed by integers, and integers are really bigints in CockroachDB. See https://www.cockroachlabs.com/docs/v19.2/serial.html#generated-values-for-mode-sql_sequence."
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
exclude :test_strict_loading_violations_are_ignored_on_fixtures, "This test is re-implemented with transactional tests disabled."
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
exclude :test_doesnt_rely_on_active_support_test_case_specific_methods, "This test is replaced with a version in our test suite."
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
exclude :test_transaction_instrumentation_with_restart_parent_transaction_on_rollback, "CRDB doesn't support ROLLBACK AND CHAIN"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
exclude :test_read_uncommitted, "CockroachDB implements all isolation levels as SERIALIZABLE so this test does not work as expected."
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
exclude :test_lookup_defaults_to_the_current_adapter, "Skipping until we can triage further. See https://github.com/cockroachdb/activerecord-cockroachdb-adapter/issues/48"
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
exclude "test_order:_allows_NULLS_FIRST_and_NULLS_LAST_too", "This functionality is not yet implemented, see https://github.com/cockroachdb/cockroach/issues/6224 for more details"
|
|
2
|
+
exclude "test_order:_allows_valid_arguments_with_COLLATE", "Replaced for correct collation name"
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
updateable_msg = "Updateable views are not currently supported, see https://github.com/cockroachdb/cockroach/issues/20948"
|
|
2
|
+
exclude :test_update_record_to_fail_view_conditions, updateable_msg
|
|
3
|
+
exclude :test_insert_record, updateable_msg
|
|
4
|
+
exclude :test_insert_record_populates_primary_key, updateable_msg
|
|
5
|
+
exclude :test_update_record, updateable_msg
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
exclude :test_has_and_belongs_to_many_with_annotation_includes_a_query_comment, "This test is overrided to fix the fixtures sequence. See test/cases/associations_test.rb"
|
|
2
|
+
exclude :test_belongs_to_with_annotation_includes_a_query_comment, "This test is overrided to fix the fixtures sequence. See test/cases/associations_test.rb"
|
|
3
|
+
exclude :test_has_one_with_annotation_includes_a_query_comment, "This test is overrided to fix the fixtures sequence. See test/cases/associations_test.rb"
|
|
4
|
+
exclude :test_has_many_with_annotation_includes_a_query_comment, "This test is overrided to fix the fixtures sequence. See test/cases/associations_test.rb"
|
|
5
|
+
exclude :test_has_many_through_with_annotation_includes_a_query_comment, "This test is overrided to fix the fixtures sequence. See test/cases/associations_test.rb"
|
|
6
|
+
exclude :test_has_many_through_with_annotation_includes_a_query_comment_when_eager_loading, "This test is overrided to fix the fixtures sequence. See test/cases/associations_test.rb"
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This is a copy of postgresql_specific_schema.rb from ActiveRecord's test
|
|
4
|
+
# suite. Statements that don't work against CockroachDB have been commented out
|
|
5
|
+
# with an explanation of why they don't work.
|
|
6
|
+
|
|
7
|
+
ActiveRecord::Schema.define do
|
|
8
|
+
ActiveRecord::TestCase.enable_extension!("uuid-ossp", connection)
|
|
9
|
+
ActiveRecord::TestCase.enable_extension!("pgcrypto", connection) if connection.supports_pgcrypto_uuid?
|
|
10
|
+
|
|
11
|
+
uuid_default = connection.supports_pgcrypto_uuid? ? {} : { default: "uuid_generate_v4()" }
|
|
12
|
+
|
|
13
|
+
create_table :chat_messages, id: :uuid, force: true, **uuid_default do |t|
|
|
14
|
+
t.text :content
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
create_table :chat_messages_custom_pk, id: false, force: true do |t|
|
|
18
|
+
t.uuid :message_id, primary_key: true, default: "uuid_generate_v4()"
|
|
19
|
+
t.text :content
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
create_table :uuid_parents, id: :uuid, force: true, **uuid_default do |t|
|
|
23
|
+
t.string :name
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
create_table :uuid_children, id: :uuid, force: true, **uuid_default do |t|
|
|
27
|
+
t.string :name
|
|
28
|
+
t.uuid :uuid_parent_id
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
create_table :defaults, force: true do |t|
|
|
32
|
+
t.virtual :virtual_stored_number, type: :integer, as: "random_number * 10", stored: true if supports_virtual_columns?
|
|
33
|
+
t.integer :random_number, default: -> { "random() * 100" }
|
|
34
|
+
t.string :ruby_on_rails, default: -> { "concat('Ruby ', 'on ', 'Rails')" }
|
|
35
|
+
t.date :modified_date, default: -> { "CURRENT_DATE" }
|
|
36
|
+
t.date :modified_date_function, default: -> { "now()" }
|
|
37
|
+
t.date :fixed_date, default: "2004-01-01"
|
|
38
|
+
t.datetime :modified_time, default: -> { "CURRENT_TIMESTAMP" }
|
|
39
|
+
t.datetime :modified_time_without_precision, precision: nil, default: -> { "CURRENT_TIMESTAMP" }
|
|
40
|
+
t.datetime :modified_time_with_precision_0, precision: 0, default: -> { "CURRENT_TIMESTAMP" }
|
|
41
|
+
t.datetime :modified_time_function, default: -> { "now()" }
|
|
42
|
+
t.datetime :fixed_time, default: "2004-01-01 00:00:00.000000-00"
|
|
43
|
+
t.timestamptz :fixed_time_with_time_zone, default: "2004-01-01 01:00:00+1"
|
|
44
|
+
t.column :char1, "char(1)", default: "Y"
|
|
45
|
+
t.string :char2, limit: 50, default: "a varchar field"
|
|
46
|
+
t.text :char3, default: "a text field"
|
|
47
|
+
t.bigint :bigint_default, default: -> { "0::bigint" }
|
|
48
|
+
t.binary :binary_default_function, default: -> { "convert_to('A', 'UTF8')" }
|
|
49
|
+
t.text :multiline_default, default: "--- []
|
|
50
|
+
|
|
51
|
+
"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
if supports_identity_columns?
|
|
55
|
+
drop_table "postgresql_identity_table", if_exists: true
|
|
56
|
+
execute <<~SQL
|
|
57
|
+
create table postgresql_identity_table (
|
|
58
|
+
id INT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY
|
|
59
|
+
)
|
|
60
|
+
SQL
|
|
61
|
+
|
|
62
|
+
drop_table "cpk_postgresql_identity_table", if_exists: true
|
|
63
|
+
execute <<~SQL
|
|
64
|
+
create table cpk_postgresql_identity_table (
|
|
65
|
+
another_id INT NOT NULL,
|
|
66
|
+
id INT NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
|
67
|
+
CONSTRAINT cpk_postgresql_identity_table_pkey PRIMARY KEY (another_id, id)
|
|
68
|
+
)
|
|
69
|
+
SQL
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
create_table :postgresql_times, force: true do |t|
|
|
73
|
+
t.interval :time_interval
|
|
74
|
+
t.interval :scaled_time_interval, precision: 6
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
create_table :postgresql_oids, force: true do |t|
|
|
78
|
+
t.oid :obj_id
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
drop_table "postgresql_timestamp_with_zones", if_exists: true
|
|
82
|
+
drop_table "postgresql_partitioned_table", if_exists: true
|
|
83
|
+
drop_table "postgresql_partitioned_table_parent", if_exists: true
|
|
84
|
+
|
|
85
|
+
execute "DROP SEQUENCE IF EXISTS companies_nonstd_seq CASCADE"
|
|
86
|
+
execute "CREATE SEQUENCE companies_nonstd_seq START 101 OWNED BY companies.id"
|
|
87
|
+
execute "ALTER TABLE companies ALTER COLUMN id SET DEFAULT nextval('companies_nonstd_seq')"
|
|
88
|
+
execute "DROP SEQUENCE IF EXISTS companies_id_seq"
|
|
89
|
+
|
|
90
|
+
execute "DROP FUNCTION IF EXISTS partitioned_insert_trigger()"
|
|
91
|
+
|
|
92
|
+
# CockroachDB uses unique_rowid() for primary keys by default instead of
|
|
93
|
+
# sequences. Therefore, there aren't any sequences to update here.
|
|
94
|
+
# See https://www.cockroachlabs.com/docs/v19.2/serial.html#modes-of-operation.
|
|
95
|
+
# %w(accounts_id_seq developers_id_seq projects_id_seq topics_id_seq customers_id_seq orders_id_seq).each do |seq_name|
|
|
96
|
+
# execute "SELECT setval('#{seq_name}', 100)"
|
|
97
|
+
# end
|
|
98
|
+
|
|
99
|
+
execute <<_SQL
|
|
100
|
+
CREATE TABLE postgresql_timestamp_with_zones (
|
|
101
|
+
id SERIAL PRIMARY KEY,
|
|
102
|
+
time TIMESTAMP WITH TIME ZONE
|
|
103
|
+
);
|
|
104
|
+
_SQL
|
|
105
|
+
|
|
106
|
+
# Table inheritance is not supported in CockroachDB.
|
|
107
|
+
# See https://go.crdb.dev/issue-v/22456/v24.1
|
|
108
|
+
# begin
|
|
109
|
+
# execute <<_SQL
|
|
110
|
+
# CREATE TABLE postgresql_partitioned_table_parent (
|
|
111
|
+
# id SERIAL PRIMARY KEY,
|
|
112
|
+
# number integer
|
|
113
|
+
# );
|
|
114
|
+
# CREATE TABLE postgresql_partitioned_table ( )
|
|
115
|
+
# INHERITS (postgresql_partitioned_table_parent);
|
|
116
|
+
|
|
117
|
+
# CREATE OR REPLACE FUNCTION partitioned_insert_trigger()
|
|
118
|
+
# RETURNS TRIGGER AS $$
|
|
119
|
+
# BEGIN
|
|
120
|
+
# INSERT INTO postgresql_partitioned_table VALUES (NEW.*);
|
|
121
|
+
# RETURN NULL;
|
|
122
|
+
# END;
|
|
123
|
+
# $$
|
|
124
|
+
# LANGUAGE plpgsql;
|
|
125
|
+
|
|
126
|
+
# CREATE TRIGGER insert_partitioning_trigger
|
|
127
|
+
# BEFORE INSERT ON postgresql_partitioned_table_parent
|
|
128
|
+
# FOR EACH ROW EXECUTE PROCEDURE partitioned_insert_trigger();
|
|
129
|
+
# _SQL
|
|
130
|
+
# rescue ActiveRecord::StatementInvalid => e
|
|
131
|
+
# if e.message.include?('language "plpgsql" does not exist')
|
|
132
|
+
# execute "CREATE LANGUAGE 'plpgsql';"
|
|
133
|
+
# retry
|
|
134
|
+
# else
|
|
135
|
+
# raise e
|
|
136
|
+
# end
|
|
137
|
+
# end
|
|
138
|
+
|
|
139
|
+
# This table is to verify if the :limit option is being ignored for text and binary columns
|
|
140
|
+
create_table :limitless_fields, force: true do |t|
|
|
141
|
+
t.binary :binary, limit: 100_000
|
|
142
|
+
t.text :text, limit: 100_000
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
create_table :bigint_array, force: true do |t|
|
|
146
|
+
t.integer :big_int_data_points, limit: 8, array: true
|
|
147
|
+
t.decimal :decimal_array_default, array: true, default: [1.23, 3.45]
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
create_table :uuid_comments, force: true, id: false do |t|
|
|
151
|
+
t.uuid :uuid, primary_key: true, **uuid_default
|
|
152
|
+
t.string :content
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
create_table :uuid_entries, force: true, id: false do |t|
|
|
156
|
+
t.uuid :uuid, primary_key: true, **uuid_default
|
|
157
|
+
t.string :entryable_type, null: false
|
|
158
|
+
t.uuid :entryable_uuid, null: false
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
create_table :uuid_items, force: true, id: false do |t|
|
|
162
|
+
t.uuid :uuid, primary_key: true, **uuid_default
|
|
163
|
+
t.string :title
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
create_table :uuid_messages, force: true, id: false do |t|
|
|
167
|
+
t.uuid :uuid, primary_key: true, **uuid_default
|
|
168
|
+
t.string :subject
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# CockroachDB does not support exclusion constraints.
|
|
172
|
+
#
|
|
173
|
+
# create_table :test_exclusion_constraints, force: true do |t|
|
|
174
|
+
# ...
|
|
175
|
+
# end
|
|
176
|
+
|
|
177
|
+
create_table :test_unique_constraints, force: true do |t|
|
|
178
|
+
t.integer :position_1
|
|
179
|
+
t.integer :position_2
|
|
180
|
+
t.integer :position_3
|
|
181
|
+
t.integer :position_4
|
|
182
|
+
|
|
183
|
+
# CockroachDB does not support deferrable, hence these four lines have been simplified.
|
|
184
|
+
t.unique_constraint :position_1, name: "test_unique_constraints_position_1"
|
|
185
|
+
t.unique_constraint :position_2, name: "test_unique_constraints_position_2"
|
|
186
|
+
t.unique_constraint :position_3, name: "test_unique_constraints_position_3"
|
|
187
|
+
t.unique_constraint :position_4, name: "test_unique_constraints_position_4"
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
if supports_partitioned_indexes?
|
|
191
|
+
create_table(:measurements, id: false, force: true, options: "PARTITION BY LIST (city_id)") do |t|
|
|
192
|
+
t.string :city_id, null: false
|
|
193
|
+
t.date :logdate, null: false
|
|
194
|
+
t.integer :peaktemp
|
|
195
|
+
t.integer :unitsales
|
|
196
|
+
t.index [:logdate, :city_id], unique: true
|
|
197
|
+
end
|
|
198
|
+
create_table(:measurements_toronto, id: false, force: true,
|
|
199
|
+
options: "PARTITION OF measurements FOR VALUES IN (1)")
|
|
200
|
+
create_table(:measurements_concepcion, id: false, force: true,
|
|
201
|
+
options: "PARTITION OF measurements FOR VALUES IN (2)")
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
add_index(:companies, [:firm_id, :type], name: "company_include_index", include: [:name, :account_id])
|
|
205
|
+
|
|
206
|
+
# In the original PostgreSQL schema, there would be a table here, populated using triggers.
|
|
207
|
+
# This is not supported by Cockroachdb so we removed that bit.
|
|
208
|
+
|
|
209
|
+
create_table :buildings, force: true do |t|
|
|
210
|
+
t.st_point :coordinates, srid: 3857
|
|
211
|
+
t.st_point :latlon, srid: 4326, geographic: true
|
|
212
|
+
t.st_polygon :boundary, srid: 3857
|
|
213
|
+
t.multi_polygon :m_poly, srid: 3857
|
|
214
|
+
t.multi_point :points, srid: 3857
|
|
215
|
+
t.line_string :path, srid: 3857
|
|
216
|
+
t.column(:shape, :geometry)
|
|
217
|
+
end
|
|
218
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "parser"
|
|
4
|
+
require "prism"
|
|
5
|
+
|
|
6
|
+
module CopyCat
|
|
7
|
+
module NoWarnRewrite
|
|
8
|
+
def warn(message, category: nil, **kwargs)
|
|
9
|
+
if /method redefined; discarding old|previous definition of/.match?(message) &&
|
|
10
|
+
caller_locations.any? { _1.path["test/support/copy_cat.rb"] && _1.label["copy_methods"] }
|
|
11
|
+
# ignore
|
|
12
|
+
else
|
|
13
|
+
super
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
::Warning.singleton_class.prepend NoWarnRewrite
|
|
18
|
+
|
|
19
|
+
extend self
|
|
20
|
+
|
|
21
|
+
# Copy methods from The original rails class to our adapter.
|
|
22
|
+
# While copying, we can rewrite the source code of the method using
|
|
23
|
+
# ast. Use `debug: true` to lead you through that process.
|
|
24
|
+
#
|
|
25
|
+
# Once debug is set, you can check the closest node you want to edit
|
|
26
|
+
# and then create a method `on_<node_type>` to handle it.
|
|
27
|
+
def copy_methods(new_klass, old_klass, *methods, debug: false, &block)
|
|
28
|
+
if debug and not block_given?
|
|
29
|
+
puts "You need to provide a block to debug."
|
|
30
|
+
end
|
|
31
|
+
methods.each do |met|
|
|
32
|
+
file, _ = old_klass.instance_method(met).source_location
|
|
33
|
+
ast = find_method(Prism::Translation::Parser.parse_file(file), met)
|
|
34
|
+
code =
|
|
35
|
+
if block_given?
|
|
36
|
+
source = ast.location.expression.source
|
|
37
|
+
buffer = Parser::Source::Buffer.new(met, source: source)
|
|
38
|
+
# We need to recompute the ast to have correct locations.
|
|
39
|
+
ast = Prism::Translation::Parser.parse(source)
|
|
40
|
+
|
|
41
|
+
if debug
|
|
42
|
+
puts "=" * 80
|
|
43
|
+
puts "Rewriter doc: https://www.rubydoc.info/gems/parser/3.3.0.5/Parser/TreeRewriter"
|
|
44
|
+
puts "Pattern matching doc: https://docs.ruby-lang.org/en/master/syntax/pattern_matching_rdoc.html"
|
|
45
|
+
puts
|
|
46
|
+
puts "Method: #{met}"
|
|
47
|
+
puts
|
|
48
|
+
puts "Source:"
|
|
49
|
+
puts buffer.source
|
|
50
|
+
puts
|
|
51
|
+
puts "AST:"
|
|
52
|
+
pp ast
|
|
53
|
+
puts
|
|
54
|
+
end
|
|
55
|
+
rewriter_class = Class.new(Parser::TreeRewriter, &block)
|
|
56
|
+
rewriter_class.new.rewrite(buffer, ast)
|
|
57
|
+
else
|
|
58
|
+
ast.location.expression.source
|
|
59
|
+
end
|
|
60
|
+
if debug and block_given?
|
|
61
|
+
puts "Rewritten source:"
|
|
62
|
+
puts code
|
|
63
|
+
puts "=" * 80
|
|
64
|
+
end
|
|
65
|
+
location = caller_locations(3, 1).first
|
|
66
|
+
new_klass.class_eval(code, location.absolute_path || location.path, location.lineno)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def find_method(ast, method_name)
|
|
71
|
+
method_name = method_name.to_sym
|
|
72
|
+
to_search = [ast]
|
|
73
|
+
while !to_search.empty?
|
|
74
|
+
node = to_search.shift
|
|
75
|
+
next unless node.is_a?(Parser::AST::Node)
|
|
76
|
+
if node in [:def, ^method_name, *]
|
|
77
|
+
return node
|
|
78
|
+
end
|
|
79
|
+
to_search += node.children
|
|
80
|
+
end
|
|
81
|
+
return nil
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Allow exclusion of tests by name using #exclude_from_transactional_tests(test_name)
|
|
4
|
+
module ExcludeFromTransactionalTests
|
|
5
|
+
module ClassMethods
|
|
6
|
+
def exclude_from_transactional_tests(name)
|
|
7
|
+
@non_transactional_list ||= []
|
|
8
|
+
@non_transactional_list << name.to_s
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def non_transactional_list
|
|
12
|
+
@non_transactional_list ||= []
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.prepended(base)
|
|
17
|
+
base.extend ClassMethods
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def before_setup
|
|
21
|
+
@old_use_transactional_tests = self.use_transactional_tests
|
|
22
|
+
if @old_use_transactional_tests # stay false if false
|
|
23
|
+
self.use_transactional_tests = !self.class.non_transactional_list.include?(@NAME.to_s)
|
|
24
|
+
end
|
|
25
|
+
super
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def after_teardown
|
|
29
|
+
super
|
|
30
|
+
ensure
|
|
31
|
+
self.use_transactional_tests = @old_use_transactional_tests
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module ARTest
|
|
2
|
+
module CockroachDB
|
|
3
|
+
|
|
4
|
+
extend self
|
|
5
|
+
|
|
6
|
+
def root_cockroachdb
|
|
7
|
+
File.expand_path File.join(File.dirname(__FILE__), '..', '..')
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def test_root_cockroachdb
|
|
11
|
+
File.join root_cockroachdb, 'test'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def root_activerecord
|
|
15
|
+
File.join Gem.loaded_specs['rails'].full_gem_path, 'activerecord'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def root_activerecord_lib
|
|
19
|
+
File.join root_activerecord, 'lib'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def root_activerecord_test
|
|
23
|
+
File.join root_activerecord, 'test'
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_load_paths
|
|
27
|
+
['lib', 'test', root_activerecord_lib, root_activerecord_test]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def add_to_load_paths!
|
|
31
|
+
test_load_paths.each { |p| $LOAD_PATH.unshift(p) unless $LOAD_PATH.include?(p) }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def arconfig_file
|
|
35
|
+
File.join test_root_cockroachdb, 'config.yml'
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def arconfig_file_env!
|
|
39
|
+
ENV['ARCONFIG'] = arconfig_file
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
ARTest::CockroachDB.add_to_load_paths!
|
|
46
|
+
ARTest::CockroachDB.arconfig_file_env!
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RakeHelpers
|
|
4
|
+
COCKROACHDB_TEST_HELPER = 'test/cases/helper_cockroachdb.rb'
|
|
5
|
+
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
# Look for TEST_FILES_AR or TEST_FILES env variables
|
|
9
|
+
# to set specific tests, otherwise load every tests
|
|
10
|
+
# from active_record and this adapter.
|
|
11
|
+
def test_files
|
|
12
|
+
ar_test_files = ENV.fetch('TEST_FILES_AR', '')
|
|
13
|
+
cr_test_files = ENV.fetch('TEST_FILES', '')
|
|
14
|
+
|
|
15
|
+
return all_test_files if ar_test_files.empty? && cr_test_files.empty?
|
|
16
|
+
|
|
17
|
+
ar_test_files = ar_test_files.
|
|
18
|
+
split(',').
|
|
19
|
+
map { |file| File.join ARTest::CockroachDB.root_activerecord, file.strip }.
|
|
20
|
+
tap { _1.prepend(COCKROACHDB_TEST_HELPER) unless _1.empty? }
|
|
21
|
+
|
|
22
|
+
cr_test_files = cr_test_files.split(',').map(&:strip)
|
|
23
|
+
|
|
24
|
+
ar_test_files + cr_test_files
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def all_test_files
|
|
28
|
+
activerecord_test_files =
|
|
29
|
+
::Rake::FileList["#{ARTest::CockroachDB.root_activerecord}/test/cases/**/*_test.rb"].
|
|
30
|
+
reject { _1.include?("/adapters/") || _1.include?("/encryption/performance") } +
|
|
31
|
+
::Rake::FileList["#{ARTest::CockroachDB.root_activerecord}/test/cases/adapters/postgresql/**/*_test.rb"]
|
|
32
|
+
|
|
33
|
+
cockroachdb_test_files = ::Rake::FileList['test/cases/**/*_test.rb']
|
|
34
|
+
|
|
35
|
+
::Rake::FileList[COCKROACHDB_TEST_HELPER] + activerecord_test_files + cockroachdb_test_files
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SQLLogger
|
|
4
|
+
module_function
|
|
5
|
+
|
|
6
|
+
def stdout_log
|
|
7
|
+
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
|
8
|
+
ActiveRecord::Base.logger.level = Logger::DEBUG
|
|
9
|
+
ActiveRecord::LogSubscriber::IGNORE_PAYLOAD_NAMES.clear
|
|
10
|
+
ActiveRecord::Base.logger.formatter = proc { |severity, time, progname, msg|
|
|
11
|
+
th = Thread.current[:name]
|
|
12
|
+
th = "THREAD=#{th}" if th
|
|
13
|
+
Logger::Formatter.new.call(severity, time, progname || th, msg)
|
|
14
|
+
}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def summary_log
|
|
18
|
+
ActiveRecord::TotalTimeSubscriber.attach_to :active_record
|
|
19
|
+
Minitest.after_run {
|
|
20
|
+
detail = ActiveRecord::TotalTimeSubscriber.hash.map { |k,v| [k, [v.sum, v.sum / v.size, v.size]]}.sort_by { |_, (_total, avg, _)| -avg }.to_h
|
|
21
|
+
time = detail.values.sum { |(total, _, _)| total } / 1_000
|
|
22
|
+
count = detail.values.sum { |(_, _, count)| count }
|
|
23
|
+
File.write(
|
|
24
|
+
"tmp/query_time.json",
|
|
25
|
+
JSON.pretty_generate(detail)
|
|
26
|
+
)
|
|
27
|
+
puts "Total time spent in SQL: #{time}s (#{count} queries)"
|
|
28
|
+
puts "Detail per query kind available in tmp/query_time.json (total time in ms, avg time in ms, query count). Sorted by avg time."
|
|
29
|
+
}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Remove content between single quotes and double quotes from keys
|
|
33
|
+
# to have a clear idea of which queries are being executed.
|
|
34
|
+
def clean_sql(sql)
|
|
35
|
+
sql.gsub(/".*?"/m, "\"...\"").gsub("''", "").gsub(/'.*?'/m, "'...'")
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class ActiveRecord::TotalTimeSubscriber < ActiveRecord::LogSubscriber
|
|
40
|
+
def self.hash
|
|
41
|
+
@@hash
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def sql(event)
|
|
45
|
+
# NOTE: If you want to debug a specific query, you can use a 'binding.irb' here with
|
|
46
|
+
# a specific condition on 'event.payload[:sql]' content.
|
|
47
|
+
#
|
|
48
|
+
# binding.irb if event.payload[:sql].include?("attr.attname, nsp.nspname")
|
|
49
|
+
#
|
|
50
|
+
@@hash ||= {}
|
|
51
|
+
key = SQLLogger.clean_sql(event.payload[:sql])
|
|
52
|
+
@@hash[key] ||= []
|
|
53
|
+
@@hash[key].push event.duration
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "activerecord-cockroachdb-adapter"
|
|
4
|
+
require_relative 'paths_cockroachdb'
|
|
5
|
+
require 'support/config' # ARTest.config
|
|
6
|
+
require 'support/connection' # ARTest.connect
|
|
7
|
+
|
|
8
|
+
module TemplateCreator
|
|
9
|
+
class DefaultDB < ActiveRecord::Base
|
|
10
|
+
establish_connection(
|
|
11
|
+
adapter: 'cockroachdb',
|
|
12
|
+
database: 'defaultdb',
|
|
13
|
+
port: 26257,
|
|
14
|
+
user: 'root',
|
|
15
|
+
host: 'localhost'
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Database created once the backup is finished to make sure we have a
|
|
20
|
+
# clean backup to work with. See #template_exists?
|
|
21
|
+
EXISTS = "exists"
|
|
22
|
+
BACKUP_DIR = "userfile://defaultdb.public/activerecord-crdb-adapter"
|
|
23
|
+
|
|
24
|
+
module_function
|
|
25
|
+
|
|
26
|
+
def template_version
|
|
27
|
+
ar_version = ActiveRecord.version.version.gsub('.','_')
|
|
28
|
+
main_schema_digest = Digest::MD5.file(SCHEMA_ROOT + "/schema.rb").hexdigest
|
|
29
|
+
crdb_schema_digest = Digest::MD5.file("#{__dir__}/../schema/cockroachdb_specific_schema.rb").hexdigest
|
|
30
|
+
"#{ar_version}_#{main_schema_digest}_#{crdb_schema_digest}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def version_backup_path
|
|
34
|
+
BACKUP_DIR + "/#{template_version}"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def template_db_name(db_name)
|
|
38
|
+
"#{db_name}__template__#{template_version}"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def template_exists?
|
|
42
|
+
template_db_exists?(EXISTS)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def databases
|
|
46
|
+
@databases ||=ARTest.config.dig("connections", "cockroachdb").map { |_, value| value["database"] }.uniq
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def with_template_db_names
|
|
50
|
+
old_crdb = ARTest.config["connections"]["cockroachdb"]
|
|
51
|
+
new_crdb = old_crdb.transform_values { _1.merge("database" => template_db_name(_1["database"])) }
|
|
52
|
+
ARTest.config["connections"]["cockroachdb"] = new_crdb
|
|
53
|
+
yield
|
|
54
|
+
ensure
|
|
55
|
+
ARTest.config["connections"]["cockroachdb"] = old_crdb
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def template_db_exists?(db_name)
|
|
59
|
+
DefaultDB.lease_connection.select_value("SELECT 1 FROM pg_database WHERE datname='#{template_db_name(db_name)}'") == 1
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def drop_template_db(db_name)
|
|
63
|
+
DefaultDB.lease_connection.execute("DROP DATABASE #{template_db_name(db_name)} CASCADE")
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def create_template_db(db_name)
|
|
67
|
+
DefaultDB.lease_connection.execute("CREATE DATABASE #{template_db_name(db_name)}")
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def create_test_template(&block)
|
|
71
|
+
databases.each do |db_name|
|
|
72
|
+
drop_template_db(db_name) if template_db_exists?(db_name)
|
|
73
|
+
create_template_db(db_name)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
with_template_db_names do
|
|
77
|
+
shh { ARTest.connect }
|
|
78
|
+
block.call
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
DefaultDB.lease_connection.execute(<<~SQL)
|
|
82
|
+
BACKUP DATABASE #{databases.map { |db| template_db_name(db) }.join(', ')}
|
|
83
|
+
INTO '#{version_backup_path}'
|
|
84
|
+
SQL
|
|
85
|
+
create_template_db(EXISTS)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def load_from_template(&block)
|
|
89
|
+
create_test_template(&block) unless template_exists?
|
|
90
|
+
databases.each do |db_name|
|
|
91
|
+
begin
|
|
92
|
+
DefaultDB.lease_connection.execute("DROP DATABASE #{db_name}")
|
|
93
|
+
rescue ActiveRecord::StatementInvalid => e
|
|
94
|
+
unless e.cause.class == PG::InvalidCatalogName
|
|
95
|
+
raise e
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
DefaultDB.lease_connection.execute("CREATE DATABASE #{db_name}")
|
|
99
|
+
DefaultDB.lease_connection.execute(<<~SQL)
|
|
100
|
+
RESTORE #{template_db_name(db_name)}.*
|
|
101
|
+
FROM LATEST IN '#{version_backup_path}'
|
|
102
|
+
WITH into_db = '#{db_name}'
|
|
103
|
+
SQL
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
private def shh
|
|
108
|
+
original_stdout = $stdout
|
|
109
|
+
$stdout = StringIO.new
|
|
110
|
+
yield
|
|
111
|
+
ensure
|
|
112
|
+
$stdout = original_stdout
|
|
113
|
+
end
|
|
114
|
+
end
|