activerecord-cockroachdb-adapter 8.0.2 → 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.
Files changed (239) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/CONTRIBUTING.md +10 -17
  4. data/Gemfile +2 -1
  5. data/Rakefile +0 -19
  6. data/lib/active_record/connection_adapters/cockroachdb/column.rb +66 -24
  7. data/lib/active_record/connection_adapters/cockroachdb/database_statements.rb +13 -3
  8. data/lib/active_record/connection_adapters/cockroachdb/database_tasks.rb +1 -1
  9. data/lib/active_record/connection_adapters/cockroachdb/oid/spatial.rb +34 -19
  10. data/lib/active_record/connection_adapters/cockroachdb/quoting.rb +19 -3
  11. data/lib/active_record/connection_adapters/cockroachdb/referential_integrity.rb +87 -55
  12. data/lib/active_record/connection_adapters/cockroachdb/schema_statements.rb +181 -102
  13. data/lib/active_record/connection_adapters/cockroachdb_adapter.rb +42 -25
  14. data/lib/version.rb +1 -1
  15. data/test/cases/adapter_test.rb +98 -0
  16. data/test/cases/adapters/cockroachdb/referential_integrity_test.rb +51 -0
  17. data/test/cases/adapters/postgresql/active_schema_test.rb +71 -0
  18. data/test/cases/adapters/postgresql/change_schema_test.rb +75 -0
  19. data/test/cases/adapters/postgresql/connection_test.rb +46 -0
  20. data/test/cases/adapters/postgresql/ddl_test.rb +326 -0
  21. data/test/cases/adapters/postgresql/interval_test.rb +131 -0
  22. data/test/cases/adapters/postgresql/nested_class_test.rb +22 -0
  23. data/test/cases/adapters/postgresql/numeric_test.rb +28 -0
  24. data/test/cases/adapters/postgresql/postgis_test.rb +185 -0
  25. data/test/cases/adapters/postgresql/postgresql_adapter_test.rb +115 -0
  26. data/test/cases/adapters/postgresql/quoting_test.rb +30 -0
  27. data/test/cases/adapters/postgresql/schema_statements_test.rb +29 -0
  28. data/test/cases/adapters/postgresql/serial_test.rb +199 -0
  29. data/test/cases/adapters/postgresql/spatial_queries_test.rb +118 -0
  30. data/test/cases/adapters/postgresql/spatial_setup_test.rb +18 -0
  31. data/test/cases/adapters/postgresql/spatial_type_test.rb +41 -0
  32. data/test/cases/adapters/postgresql/timestamp_test.rb +58 -0
  33. data/test/cases/adapters/postgresql/virtual_column_test.rb +39 -0
  34. data/test/cases/associations/eager_load_nested_include_test.rb +111 -0
  35. data/test/cases/associations/left_outer_join_association_test.rb +30 -0
  36. data/test/cases/associations_test.rb +108 -0
  37. data/test/cases/base_test.rb +31 -0
  38. data/test/cases/comment_test.rb +75 -0
  39. data/test/cases/connection_adapters/type_test.rb +28 -0
  40. data/test/cases/database_configurations/resolver_test.rb +24 -0
  41. data/test/cases/defaults_test.rb +45 -0
  42. data/test/cases/dirty_test.rb +24 -0
  43. data/test/cases/fixtures_test.rb +541 -0
  44. data/test/cases/helper_cockroachdb.rb +232 -0
  45. data/test/cases/inheritance_test.rb +42 -0
  46. data/test/cases/invertible_migration_test.rb +73 -0
  47. data/test/cases/marshal_serialization_test.rb +45 -0
  48. data/test/cases/migration/change_schema_test.rb +140 -0
  49. data/test/cases/migration/check_constraint_test.rb +125 -0
  50. data/test/cases/migration/columns_test.rb +50 -0
  51. data/test/cases/migration/create_join_table_test.rb +66 -0
  52. data/test/cases/migration/foreign_key_test.rb +390 -0
  53. data/test/cases/migration/hidden_column_test.rb +70 -0
  54. data/test/cases/migration/references_foreign_key_test.rb +124 -0
  55. data/test/cases/migration_test.rb +120 -0
  56. data/test/cases/persistence_test.rb +33 -0
  57. data/test/cases/primary_keys_test.rb +134 -0
  58. data/test/cases/relation/aost_test.rb +57 -0
  59. data/test/cases/relation/or_test.rb +26 -0
  60. data/test/cases/relation/table_hints_test.rb +124 -0
  61. data/test/cases/relation_test.rb +26 -0
  62. data/test/cases/relations_test.rb +29 -0
  63. data/test/cases/schema_dumper_test.rb +221 -0
  64. data/test/cases/show_create_test.rb +14 -0
  65. data/test/cases/strict_loading_test.rb +34 -0
  66. data/test/cases/tasks/cockroachdb_rake_test.rb +113 -0
  67. data/test/cases/test_fixtures_test.rb +57 -0
  68. data/test/cases/transactions_test.rb +51 -0
  69. data/test/cases/unsafe_raw_sql_test.rb +22 -0
  70. data/test/config.yml +23 -0
  71. data/test/excludes/ActiveRecord/AdapterTest.rb +3 -0
  72. data/test/excludes/ActiveRecord/AdapterTestWithoutTransaction.rb +25 -0
  73. data/test/excludes/ActiveRecord/ConnectionAdapters/PoolConfig/ResolverTest.rb +1 -0
  74. data/test/excludes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter/BindParameterTest.rb +15 -0
  75. data/test/excludes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter/QuotingTest.rb +22 -0
  76. data/test/excludes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapterPreventWritesLegacyTest.rb +1 -0
  77. data/test/excludes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapterPreventWritesTest.rb +1 -0
  78. data/test/excludes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapterTest.rb +40 -0
  79. data/test/excludes/ActiveRecord/ConnectionAdapters/RegistrationIsolatedTest.rb +14 -0
  80. data/test/excludes/ActiveRecord/Encryption/EncryptionPerformanceTest.rb +1 -0
  81. data/test/excludes/ActiveRecord/Encryption/EnvelopeEncryptionPerformanceTest.rb +1 -0
  82. data/test/excludes/ActiveRecord/Encryption/ExtendedDeterministicQueriesPerformanceTest.rb +1 -0
  83. data/test/excludes/ActiveRecord/Encryption/StoragePerformanceTest.rb +2 -0
  84. data/test/excludes/ActiveRecord/InstrumentationTest.rb +16 -0
  85. data/test/excludes/ActiveRecord/InvertibleMigrationTest.rb +2 -0
  86. data/test/excludes/ActiveRecord/Migration/ChangeSchemaTest.rb +7 -0
  87. data/test/excludes/ActiveRecord/Migration/CheckConstraintTest.rb +6 -0
  88. data/test/excludes/ActiveRecord/Migration/ColumnsTest.rb +4 -0
  89. data/test/excludes/ActiveRecord/Migration/CompatibilityTest.rb +50 -0
  90. data/test/excludes/ActiveRecord/Migration/CompositeForeignKeyTest.rb +2 -0
  91. data/test/excludes/ActiveRecord/Migration/CreateJoinTableTest.rb +2 -0
  92. data/test/excludes/ActiveRecord/Migration/ForeignKeyInCreateTest.rb +1 -0
  93. data/test/excludes/ActiveRecord/Migration/ForeignKeyTest.rb +35 -0
  94. data/test/excludes/ActiveRecord/Migration/InvalidOptionsTest.rb +14 -0
  95. data/test/excludes/ActiveRecord/Migration/PGChangeSchemaTest.rb +8 -0
  96. data/test/excludes/ActiveRecord/Migration/ReferencesForeignKeyTest.rb +7 -0
  97. data/test/excludes/ActiveRecord/Migration/ReferencesIndexTest.rb +7 -0
  98. data/test/excludes/ActiveRecord/Migration/ReferencesStatementsTest.rb +3 -0
  99. data/test/excludes/ActiveRecord/MysqlDBCreateWithInvalidPermissionsTest.rb +1 -0
  100. data/test/excludes/ActiveRecord/OrTest.rb +1 -0
  101. data/test/excludes/ActiveRecord/PostgreSQLStructureDumpTest.rb +10 -0
  102. data/test/excludes/ActiveRecord/PostgresqlConnectionTest.rb +12 -0
  103. data/test/excludes/ActiveRecord/PostgresqlTransactionNestedTest.rb +14 -0
  104. data/test/excludes/ActiveRecord/PostgresqlTransactionTest.rb +4 -0
  105. data/test/excludes/ActiveRecord/RelationTest.rb +2 -0
  106. data/test/excludes/ActiveRecord/TooManyOrTest.rb +1 -0
  107. data/test/excludes/ActiveSupportSubclassWithFixturesTest.rb +1 -0
  108. data/test/excludes/AssociationDeprecationTest/NotifyModeTest.rb +2 -0
  109. data/test/excludes/AssociationDeprecationTest/RaiseBacktraceModeTest.rb +2 -0
  110. data/test/excludes/AssociationDeprecationTest/RaiseModeTest.rb +2 -0
  111. data/test/excludes/AssociationDeprecationTest/WarnBacktraceModeTest.rb +2 -0
  112. data/test/excludes/AssociationDeprecationTest/WarnModeTest.rb +2 -0
  113. data/test/excludes/AssociationDeprecationTest/fix_backtrace_cleaner.rb +10 -0
  114. data/test/excludes/BasicsTest.rb +1 -0
  115. data/test/excludes/BulkAlterTableMigrationsTest.rb +7 -0
  116. data/test/excludes/CalculationsTest.rb +4 -0
  117. data/test/excludes/CommentTest.rb +2 -0
  118. data/test/excludes/CoreTest.rb +1 -0
  119. data/test/excludes/CreateOrFindByWithinTransactions.rb +3 -0
  120. data/test/excludes/DefaultsUsingMultipleSchemasAndDomainTest.rb +7 -0
  121. data/test/excludes/DirtyTest.rb +3 -0
  122. data/test/excludes/EachTest.rb +8 -0
  123. data/test/excludes/EagerLoadPolyAssocsTest.rb +1 -0
  124. data/test/excludes/ExplicitlyNamedIndexMigrationTest.rb +1 -0
  125. data/test/excludes/FixturesResetPkSequenceTest.rb +3 -0
  126. data/test/excludes/FixturesTest.rb +21 -0
  127. data/test/excludes/FixturesWithForeignKeyViolationsTest.rb +2 -0
  128. data/test/excludes/ForeignTableTest.rb +10 -0
  129. data/test/excludes/InheritanceComputeTypeTest.rb +1 -0
  130. data/test/excludes/LeftOuterJoinAssociationTest.rb +2 -0
  131. data/test/excludes/LegacyPrimaryKeyTest/V4_2.rb +6 -0
  132. data/test/excludes/LegacyPrimaryKeyTest/V5_0.rb +6 -0
  133. data/test/excludes/MarshalSerializationTest.rb +4 -0
  134. data/test/excludes/MaterializedViewTest.rb +13 -0
  135. data/test/excludes/MigrationTest.rb +2 -0
  136. data/test/excludes/MultiDbMigratorTest.rb +2 -0
  137. data/test/excludes/NestedRelationScopingTest.rb +1 -0
  138. data/test/excludes/OrTest.rb +1 -0
  139. data/test/excludes/PersistenceTest.rb +3 -0
  140. data/test/excludes/PessimisticLockingTest.rb +1 -0
  141. data/test/excludes/PostgreSQLExplainTest.rb +7 -0
  142. data/test/excludes/PostgreSQLGeometricLineTest.rb +3 -0
  143. data/test/excludes/PostgreSQLGeometricTypesTest.rb +7 -0
  144. data/test/excludes/PostgreSQLPartitionsTest.rb +1 -0
  145. data/test/excludes/PostgreSQLReferentialIntegrityTest.rb +14 -0
  146. data/test/excludes/PostgresqlArrayTest.rb +21 -0
  147. data/test/excludes/PostgresqlBigSerialTest.rb +7 -0
  148. data/test/excludes/PostgresqlBitStringTest.rb +2 -0
  149. data/test/excludes/PostgresqlByteaTest.rb +1 -0
  150. data/test/excludes/PostgresqlCitextTest.rb +7 -0
  151. data/test/excludes/PostgresqlCollationTest.rb +5 -0
  152. data/test/excludes/PostgresqlCompositeTest.rb +2 -0
  153. data/test/excludes/PostgresqlCompositeWithCustomOIDTest.rb +2 -0
  154. data/test/excludes/PostgresqlDataTypeTest.rb +9 -0
  155. data/test/excludes/PostgresqlDefaultExpressionTest.rb +1 -0
  156. data/test/excludes/PostgresqlDeferredConstraintsTest.rb +3 -0
  157. data/test/excludes/PostgresqlDomainTest.rb +2 -0
  158. data/test/excludes/PostgresqlExtensionMigrationTest.rb +5 -0
  159. data/test/excludes/PostgresqlFullTextTest.rb +3 -0
  160. data/test/excludes/PostgresqlGeometricTest.rb +4 -0
  161. data/test/excludes/PostgresqlHstoreTest.rb +45 -0
  162. data/test/excludes/PostgresqlInfinityTest.rb +5 -0
  163. data/test/excludes/PostgresqlIntervalTest.rb +1 -0
  164. data/test/excludes/PostgresqlJSONBTest.rb +27 -0
  165. data/test/excludes/PostgresqlJSONTest.rb +27 -0
  166. data/test/excludes/PostgresqlLtreeTest.rb +4 -0
  167. data/test/excludes/PostgresqlMoneyTest.rb +12 -0
  168. data/test/excludes/PostgresqlNetworkTest.rb +69 -0
  169. data/test/excludes/PostgresqlNumberTest.rb +1 -0
  170. data/test/excludes/PostgresqlPointTest.rb +15 -0
  171. data/test/excludes/PostgresqlRangeTest.rb +3 -0
  172. data/test/excludes/PostgresqlRenameTableTest.rb +2 -0
  173. data/test/excludes/PostgresqlSerialTest.rb +7 -0
  174. data/test/excludes/PostgresqlTimestampFixtureTest.rb +2 -0
  175. data/test/excludes/PostgresqlTimestampMigrationTest.rb +3 -0
  176. data/test/excludes/PostgresqlTypeLookupTest.rb +2 -0
  177. data/test/excludes/PostgresqlUUIDGenerationTest.rb +7 -0
  178. data/test/excludes/PostgresqlUUIDTest.rb +1 -0
  179. data/test/excludes/PostgresqlVirtualColumnTest.rb +17 -0
  180. data/test/excludes/PostgresqlXMLTest.rb +5 -0
  181. data/test/excludes/PrimaryKeyIntegerNilDefaultTest.rb +1 -0
  182. data/test/excludes/PrimaryKeyIntegerTest.rb +3 -0
  183. data/test/excludes/PrimaryKeysTest.rb +2 -0
  184. data/test/excludes/RelationMergingTest.rb +15 -0
  185. data/test/excludes/RelationTest.rb +3 -0
  186. data/test/excludes/ReservedWordsMigrationTest.rb +1 -0
  187. data/test/excludes/SameNameDifferentDatabaseFixturesTest.rb +1 -0
  188. data/test/excludes/SanitizeTest.rb +13 -0
  189. data/test/excludes/SchemaAuthorizationTest.rb +8 -0
  190. data/test/excludes/SchemaCreateTableOptionsTest.rb +2 -0
  191. data/test/excludes/SchemaDumperDefaultsTest.rb +1 -0
  192. data/test/excludes/SchemaDumperTest.rb +11 -0
  193. data/test/excludes/SchemaForeignKeyTest.rb +1 -0
  194. data/test/excludes/SchemaIndexNullsOrderTest.rb +2 -0
  195. data/test/excludes/SchemaIndexOpclassTest.rb +3 -0
  196. data/test/excludes/SchemaTest.rb +49 -0
  197. data/test/excludes/SequenceNameDetectionTestCases/CollidedSequenceNameTest.rb +1 -0
  198. data/test/excludes/SequenceNameDetectionTestCases/LongerSequenceNameDetectionTest.rb +1 -0
  199. data/test/excludes/StrictLoadingFixturesTest.rb +1 -0
  200. data/test/excludes/TestFixturesTest.rb +1 -0
  201. data/test/excludes/TransactionInstrumentationTest.rb +1 -0
  202. data/test/excludes/TransactionIsolationTest.rb +1 -0
  203. data/test/excludes/TypeTest.rb +1 -0
  204. data/test/excludes/UniquenessValidationTest.rb +2 -0
  205. data/test/excludes/UnloggedTablesTest.rb +3 -0
  206. data/test/excludes/UnsafeRawSqlTest.rb +2 -0
  207. data/test/excludes/UpdateableViewTest.rb +5 -0
  208. data/test/excludes/WithAnnotationsTest.rb +6 -0
  209. data/test/models/building.rb +4 -0
  210. data/test/models/spatial_model.rb +5 -0
  211. data/test/schema/cockroachdb_specific_schema.rb +218 -0
  212. data/test/support/copy_cat.rb +83 -0
  213. data/test/support/exclude_from_transactional_tests.rb +33 -0
  214. data/test/support/paths_cockroachdb.rb +46 -0
  215. data/test/support/rake_helpers.rb +37 -0
  216. data/test/support/sql_logger.rb +55 -0
  217. data/test/support/template_creator.rb +114 -0
  218. metadata +422 -34
  219. data/.editorconfig +0 -7
  220. data/.github/issue_template.md +0 -46
  221. data/.github/reproduction_scripts/migrations.rb +0 -60
  222. data/.github/reproduction_scripts/models_and_database.rb +0 -49
  223. data/.github/workflows/ci.yml +0 -96
  224. data/.github/workflows/docker.yml +0 -57
  225. data/.gitignore +0 -18
  226. data/.gitmodules +0 -0
  227. data/activerecord-cockroachdb-adapter.gemspec +0 -38
  228. data/bin/console +0 -36
  229. data/bin/console_schemas/default.rb +0 -11
  230. data/bin/console_schemas/schemas.rb +0 -23
  231. data/bin/setup +0 -8
  232. data/bin/start-cockroachdb +0 -33
  233. data/build/Dockerfile +0 -14
  234. data/build/config.teamcity.yml +0 -31
  235. data/build/local-test.sh +0 -32
  236. data/build/teamcity-test.sh +0 -76
  237. data/docker.sh +0 -35
  238. data/lib/active_record/connection_adapters/cockroachdb/spatial_column_info.rb +0 -60
  239. data/setup.sql +0 -18
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cases/migration/helper"
4
+
5
+ module ActiveRecord
6
+ module CockroachDB
7
+ class Migration
8
+ class ColumnsTest < ActiveRecord::TestCase
9
+ include ActiveRecord::Migration::TestHelper
10
+
11
+ self.use_transactional_tests = false
12
+
13
+ # This file replaces the same tests that have been excluded from ColumnsTest
14
+ # (see test/excludes/ActiveRecord/Migration/ColumnsTest.rb). New, unsaved
15
+ # records won't have string default values if the default has been changed
16
+ # in the database. This happens because once a column default is changed
17
+ # in CockroachDB, the type information on the value is missing.
18
+ # We can still verify the desired behavior by persisting the test records.
19
+ # When ActiveRecord fetches the records from the database, they'll have
20
+ # their default values.
21
+
22
+ def test_change_column_default
23
+ add_column "test_models", "first_name", :string
24
+ connection.change_column_default "test_models", "first_name", "Tester"
25
+ TestModel.reset_column_information
26
+
27
+ # Instead of using an unsaved TestModel record, persist one and fetch
28
+ # it from the database to get the new default value for type.
29
+
30
+ TestModel.create!
31
+ test_model = TestModel.last
32
+ assert_equal "Tester", test_model.first_name
33
+ end
34
+
35
+ def test_change_column_default_with_from_and_to
36
+ add_column "test_models", "first_name", :string
37
+ connection.change_column_default "test_models", "first_name", from: nil, to: "Tester"
38
+ TestModel.reset_column_information
39
+
40
+ # Instead of using an unsaved TestModel record, persist one and fetch
41
+ # it from the database to get the new default value for type.
42
+
43
+ TestModel.create!
44
+ test_model = TestModel.last
45
+ assert_equal "Tester", test_model.first_name
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cases/helper"
4
+
5
+ module ActiveRecord
6
+ module CockroachDB
7
+ class Migration
8
+ class CreateJoinTableTest < ActiveRecord::TestCase
9
+ attr_reader :connection
10
+
11
+ self.use_transactional_tests = false
12
+
13
+ def setup
14
+ super
15
+ @connection = ActiveRecord::Base.lease_connection
16
+ end
17
+
18
+ teardown do
19
+ %w(artists_musics musics_videos catalog).each do |table_name|
20
+ connection.drop_table table_name, if_exists: true
21
+ end
22
+ end
23
+
24
+ # This test is identical to the one found in Rails, save for the fact
25
+ # that transactions are turned off for test runs. It is necessary to disable
26
+ # transactional tests in order to assert on schema changes due to how
27
+ # CockroachDB handles transactions.
28
+ def test_create_join_table_with_index
29
+ connection.create_join_table :artists, :musics do |t|
30
+ t.index [:artist_id, :music_id]
31
+ end
32
+
33
+ assert_equal [%w(artist_id music_id)], connection.indexes(:artists_musics).map(&:columns)
34
+ end
35
+
36
+ # This test is identical to the one found in Rails, save for the fact
37
+ # that transactions are turned off for test runs. It is necessary to disable
38
+ # transactional tests in order to assert on schema changes due to how
39
+ # CockroachDB handles transactions.
40
+ def test_create_and_drop_join_table_with_common_prefix
41
+ with_table_cleanup do
42
+ connection.create_join_table "audio_artists", "audio_musics"
43
+ assert connection.table_exists?("audio_artists_musics")
44
+
45
+ connection.drop_join_table "audio_artists", "audio_musics"
46
+ assert !connection.table_exists?("audio_artists_musics"), "Should have dropped join table, but didn't"
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def with_table_cleanup
53
+ tables_before = connection.data_sources
54
+
55
+ yield
56
+ ensure
57
+ tables_after = connection.data_sources - tables_before
58
+
59
+ tables_after.each do |table|
60
+ connection.drop_table table
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,390 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cases/helper"
4
+ require "support/schema_dumping_helper"
5
+
6
+ module ActiveRecord
7
+ module CockroachDB
8
+ class Migration
9
+ class ForeignKeyTest < ActiveRecord::TestCase
10
+ include SchemaDumpingHelper
11
+ include ActiveSupport::Testing::Stream
12
+
13
+ # All of the following test cases are identical to the ones found in
14
+ # Rails, the only difference being that we have turned off transactional
15
+ # tests in the following line in order to properly assert on the various
16
+ # changes being tested.
17
+ self.use_transactional_tests = false
18
+
19
+ class Rocket < ActiveRecord::Base
20
+ end
21
+
22
+ class Astronaut < ActiveRecord::Base
23
+ end
24
+
25
+ setup do
26
+ @connection = ActiveRecord::Base.lease_connection
27
+ @connection.create_table "rockets", force: true do |t|
28
+ t.string :name
29
+ end
30
+
31
+ @connection.create_table "astronauts", force: true do |t|
32
+ t.string :name
33
+ t.references :rocket
34
+ end
35
+ end
36
+
37
+ teardown do
38
+ @connection.drop_table "astronauts", if_exists: true
39
+ @connection.drop_table "rockets", if_exists: true
40
+ end
41
+
42
+ def test_add_foreign_key_inferes_column
43
+ @connection.add_foreign_key :astronauts, :rockets
44
+
45
+ foreign_keys = @connection.foreign_keys("astronauts")
46
+ assert_equal 1, foreign_keys.size
47
+
48
+ fk = foreign_keys.first
49
+ assert_equal "astronauts", fk.from_table
50
+ assert_equal "rockets", fk.to_table
51
+ assert_equal "rocket_id", fk.column
52
+ assert_equal "id", fk.primary_key
53
+ assert_equal("fk_rails_78146ddd2e", fk.name)
54
+ end
55
+
56
+ def test_add_foreign_key_with_column
57
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id"
58
+
59
+ foreign_keys = @connection.foreign_keys("astronauts")
60
+ assert_equal 1, foreign_keys.size
61
+
62
+ fk = foreign_keys.first
63
+ assert_equal "astronauts", fk.from_table
64
+ assert_equal "rockets", fk.to_table
65
+ assert_equal "rocket_id", fk.column
66
+ assert_equal "id", fk.primary_key
67
+ assert_equal("fk_rails_78146ddd2e", fk.name)
68
+ end
69
+
70
+ def test_add_on_delete_restrict_foreign_key
71
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", on_delete: :restrict
72
+
73
+ foreign_keys = @connection.foreign_keys("astronauts")
74
+ assert_equal 1, foreign_keys.size
75
+
76
+ fk = foreign_keys.first
77
+ assert_equal :restrict, fk.on_delete
78
+ end
79
+
80
+ def test_add_on_delete_cascade_foreign_key
81
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", on_delete: :cascade
82
+
83
+ foreign_keys = @connection.foreign_keys("astronauts")
84
+ assert_equal 1, foreign_keys.size
85
+
86
+ fk = foreign_keys.first
87
+ assert_equal :cascade, fk.on_delete
88
+ end
89
+
90
+ def test_add_on_delete_nullify_foreign_key
91
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", on_delete: :nullify
92
+
93
+ foreign_keys = @connection.foreign_keys("astronauts")
94
+ assert_equal 1, foreign_keys.size
95
+
96
+ fk = foreign_keys.first
97
+ assert_equal :nullify, fk.on_delete
98
+ end
99
+
100
+ def test_add_foreign_key_with_on_update
101
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", on_update: :nullify
102
+
103
+ foreign_keys = @connection.foreign_keys("astronauts")
104
+ assert_equal 1, foreign_keys.size
105
+
106
+ fk = foreign_keys.first
107
+ assert_equal :nullify, fk.on_update
108
+ end
109
+
110
+ def test_foreign_key_exists
111
+ @connection.add_foreign_key :astronauts, :rockets
112
+
113
+ assert @connection.foreign_key_exists?(:astronauts, :rockets)
114
+ assert_not @connection.foreign_key_exists?(:astronauts, :stars)
115
+ end
116
+
117
+ def test_foreign_key_exists_by_column
118
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id"
119
+
120
+ assert @connection.foreign_key_exists?(:astronauts, column: "rocket_id")
121
+ assert_not @connection.foreign_key_exists?(:astronauts, column: "star_id")
122
+ end
123
+
124
+ def test_foreign_key_exists_by_name
125
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", name: "fancy_named_fk"
126
+
127
+ assert @connection.foreign_key_exists?(:astronauts, name: "fancy_named_fk")
128
+ assert_not @connection.foreign_key_exists?(:astronauts, name: "other_fancy_named_fk")
129
+ end
130
+
131
+ def test_remove_foreign_key_inferes_column
132
+ @connection.add_foreign_key :astronauts, :rockets
133
+
134
+ assert_equal 1, @connection.foreign_keys("astronauts").size
135
+ @connection.remove_foreign_key :astronauts, :rockets
136
+ assert_equal [], @connection.foreign_keys("astronauts")
137
+ end
138
+
139
+ def test_remove_foreign_key_by_column
140
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id"
141
+
142
+ assert_equal 1, @connection.foreign_keys("astronauts").size
143
+ @connection.remove_foreign_key :astronauts, column: "rocket_id"
144
+ assert_equal [], @connection.foreign_keys("astronauts")
145
+ end
146
+
147
+ def test_remove_foreign_key_by_symbol_column
148
+ @connection.add_foreign_key :astronauts, :rockets, column: :rocket_id
149
+
150
+ assert_equal 1, @connection.foreign_keys("astronauts").size
151
+ @connection.remove_foreign_key :astronauts, column: :rocket_id
152
+ assert_equal [], @connection.foreign_keys("astronauts")
153
+ end
154
+
155
+ def test_remove_foreign_key_by_name
156
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", name: "fancy_named_fk"
157
+
158
+ assert_equal 1, @connection.foreign_keys("astronauts").size
159
+ @connection.remove_foreign_key :astronauts, name: "fancy_named_fk"
160
+ assert_equal [], @connection.foreign_keys("astronauts")
161
+ end
162
+
163
+ def test_remove_foreign_key_by_the_select_one_on_the_same_table
164
+ @connection.add_foreign_key :astronauts, :rockets
165
+ @connection.add_reference :astronauts, :myrocket, foreign_key: { to_table: :rockets }
166
+
167
+ assert_equal 2, @connection.foreign_keys("astronauts").size
168
+
169
+ @connection.remove_foreign_key :astronauts, :rockets, column: "myrocket_id"
170
+
171
+ assert_equal [["astronauts", "rockets", "rocket_id"]],
172
+ @connection.foreign_keys("astronauts").map { |fk| [fk.from_table, fk.to_table, fk.column] }
173
+ end
174
+
175
+ def test_add_invalid_foreign_key
176
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", validate: false
177
+
178
+ foreign_keys = @connection.foreign_keys("astronauts")
179
+ assert_equal 1, foreign_keys.size
180
+
181
+ fk = foreign_keys.first
182
+ assert_not_predicate fk, :validated?
183
+ end
184
+
185
+ def test_validate_foreign_key_infers_column
186
+ @connection.add_foreign_key :astronauts, :rockets, validate: false
187
+ assert_not_predicate @connection.foreign_keys("astronauts").first, :validated?
188
+
189
+ @connection.validate_foreign_key :astronauts, :rockets
190
+ assert_predicate @connection.foreign_keys("astronauts").first, :validated?
191
+ end
192
+
193
+ def test_validate_foreign_key_by_column
194
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", validate: false
195
+ assert_not_predicate @connection.foreign_keys("astronauts").first, :validated?
196
+
197
+ @connection.validate_foreign_key :astronauts, column: "rocket_id"
198
+ assert_predicate @connection.foreign_keys("astronauts").first, :validated?
199
+ end
200
+
201
+ def test_validate_foreign_key_by_symbol_column
202
+ @connection.add_foreign_key :astronauts, :rockets, column: :rocket_id, validate: false
203
+ assert_not_predicate @connection.foreign_keys("astronauts").first, :validated?
204
+
205
+ @connection.validate_foreign_key :astronauts, column: :rocket_id
206
+ assert_predicate @connection.foreign_keys("astronauts").first, :validated?
207
+ end
208
+
209
+ def test_validate_foreign_key_by_name
210
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", name: "fancy_named_fk", validate: false
211
+ assert_not_predicate @connection.foreign_keys("astronauts").first, :validated?
212
+
213
+ @connection.validate_foreign_key :astronauts, name: "fancy_named_fk"
214
+ assert_predicate @connection.foreign_keys("astronauts").first, :validated?
215
+ end
216
+
217
+ def test_validate_constraint_by_name
218
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", name: "fancy_named_fk", validate: false
219
+
220
+ @connection.validate_constraint :astronauts, "fancy_named_fk"
221
+ assert_predicate @connection.foreign_keys("astronauts").first, :validated?
222
+ end
223
+
224
+ def test_schema_dumping
225
+ @connection.add_foreign_key :astronauts, :rockets
226
+ output = dump_table_schema "astronauts"
227
+ assert_match %r{\s+add_foreign_key "astronauts", "rockets"$}, output
228
+ end
229
+
230
+ class CreateCitiesAndHousesMigration < ActiveRecord::Migration::Current
231
+ def change
232
+ create_table("cities") { |t| }
233
+
234
+ create_table("houses") do |t|
235
+ t.references :city
236
+ end
237
+ add_foreign_key :houses, :cities, column: "city_id"
238
+
239
+ # remove and re-add to test that schema is updated and not accidentally cached
240
+ remove_foreign_key :houses, :cities
241
+ add_foreign_key :houses, :cities, column: "city_id", on_delete: :cascade
242
+ end
243
+ end
244
+
245
+ def test_add_foreign_key_is_reversible
246
+ migration = CreateCitiesAndHousesMigration.new
247
+ silence_stream($stdout) { migration.migrate(:up) }
248
+ assert_equal 1, @connection.foreign_keys("houses").size
249
+ ensure
250
+ silence_stream($stdout) { migration.migrate(:down) }
251
+ end
252
+
253
+ def test_foreign_key_constraint_is_not_cached_incorrectly
254
+ migration = CreateCitiesAndHousesMigration.new
255
+ silence_stream($stdout) { migration.migrate(:up) }
256
+ output = dump_table_schema "houses"
257
+ assert_match %r{\s+add_foreign_key "houses",.+on_delete: :cascade$}, output
258
+ ensure
259
+ silence_stream($stdout) { migration.migrate(:down) }
260
+ end
261
+
262
+ class CreateSchoolsAndClassesMigration < ActiveRecord::Migration::Current
263
+ def change
264
+ create_table(:schools)
265
+
266
+ create_table(:classes) do |t|
267
+ t.references :school
268
+ end
269
+ add_foreign_key :classes, :schools
270
+ end
271
+ end
272
+
273
+ def test_add_foreign_key_with_prefix
274
+ ActiveRecord::Base.table_name_prefix = "p_"
275
+ migration = CreateSchoolsAndClassesMigration.new
276
+ silence_stream($stdout) { migration.migrate(:up) }
277
+ assert_equal 1, @connection.foreign_keys("p_classes").size
278
+ ensure
279
+ silence_stream($stdout) { migration.migrate(:down) }
280
+ ActiveRecord::Base.table_name_prefix = nil
281
+ end
282
+
283
+ def test_add_foreign_key_with_suffix
284
+ ActiveRecord::Base.table_name_suffix = "_s"
285
+ migration = CreateSchoolsAndClassesMigration.new
286
+ silence_stream($stdout) { migration.migrate(:up) }
287
+ assert_equal 1, @connection.foreign_keys("classes_s").size
288
+ ensure
289
+ silence_stream($stdout) { migration.migrate(:down) }
290
+ ActiveRecord::Base.table_name_suffix = nil
291
+ end
292
+
293
+ def test_remove_foreign_key_with_if_exists_not_set
294
+ @connection.add_foreign_key :astronauts, :rockets
295
+ assert_equal 1, @connection.foreign_keys("astronauts").size
296
+
297
+ @connection.remove_foreign_key :astronauts, :rockets
298
+ assert_equal [], @connection.foreign_keys("astronauts")
299
+
300
+ error = assert_raises do
301
+ @connection.remove_foreign_key :astronauts, :rockets
302
+ end
303
+
304
+ assert_equal("Table 'astronauts' has no foreign key for rockets", error.message)
305
+ end
306
+
307
+ def test_remove_foreign_key_with_if_exists_set
308
+ @connection.add_foreign_key :astronauts, :rockets
309
+ assert_equal 1, @connection.foreign_keys("astronauts").size
310
+
311
+ @connection.remove_foreign_key :astronauts, :rockets
312
+ assert_equal [], @connection.foreign_keys("astronauts")
313
+
314
+ assert_nothing_raised do
315
+ @connection.remove_foreign_key :astronauts, :rockets, if_exists: true
316
+ end
317
+ end
318
+
319
+ def test_add_foreign_key_with_if_not_exists_not_set
320
+ @connection.add_foreign_key :astronauts, :rockets
321
+ assert_equal 1, @connection.foreign_keys("astronauts").size
322
+
323
+ if current_adapter?(:SQLite3Adapter)
324
+ assert_nothing_raised do
325
+ @connection.add_foreign_key :astronauts, :rockets
326
+ end
327
+ else
328
+ error = assert_raises do
329
+ @connection.add_foreign_key :astronauts, :rockets
330
+ end
331
+
332
+ if current_adapter?(:Mysql2Adapter)
333
+ if ActiveRecord::Base.lease_connection.mariadb?
334
+ assert_match(/Duplicate key on write or update/, error.message)
335
+ elsif ActiveRecord::Base.lease_connection.database_version < "5.6"
336
+ assert_match(/Can't create table/, error.message)
337
+ else
338
+ assert_match(/Duplicate foreign key constraint name/, error.message)
339
+ end
340
+ else
341
+ assert_match(/PG::DuplicateObject: ERROR: duplicate constraint name:*./, error.message)
342
+ end
343
+
344
+ end
345
+ end
346
+
347
+ def test_add_foreign_key_with_if_not_exists_set
348
+ @connection.add_foreign_key :astronauts, :rockets
349
+ assert_equal 1, @connection.foreign_keys("astronauts").size
350
+
351
+ assert_nothing_raised do
352
+ @connection.add_foreign_key :astronauts, :rockets, if_not_exists: true
353
+ end
354
+ end
355
+ end
356
+
357
+ class CompositeForeignKeyTest < ActiveRecord::TestCase
358
+ include SchemaDumpingHelper
359
+
360
+ self.use_transactional_tests = false
361
+
362
+ setup do
363
+ @connection = ActiveRecord::Base.lease_connection
364
+ @connection.create_table :rockets, primary_key: [:tenant_id, :id], force: true do |t|
365
+ t.integer :tenant_id
366
+ t.integer :id
367
+ end
368
+ @connection.create_table :astronauts, force: true do |t|
369
+ t.integer :rocket_id
370
+ t.integer :rocket_tenant_id
371
+ end
372
+ end
373
+
374
+ teardown do
375
+ @connection.drop_table :astronauts, if_exists: true
376
+ @connection.drop_table :rockets, if_exists: true
377
+ end
378
+
379
+ # OVERRIDE: CockroachDB does not quote the table name.
380
+ def test_add_composite_foreign_key_raises_without_options
381
+ error = assert_raises(ActiveRecord::StatementInvalid) do
382
+ @connection.add_foreign_key :astronauts, :rockets
383
+ end
384
+
385
+ assert_match(/there is no unique constraint matching given keys for referenced table rockets/, error.message)
386
+ end
387
+ end
388
+ end
389
+ end
390
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cases/helper"
4
+ require "support/schema_dumping_helper"
5
+
6
+ module ActiveRecord
7
+ module CockroachDB
8
+ class Migration
9
+ class HiddenColumnTest < ActiveRecord::TestCase
10
+ include SchemaDumpingHelper
11
+ include ActiveSupport::Testing::Stream
12
+
13
+ self.use_transactional_tests = false
14
+
15
+ class Rocket < ActiveRecord::Base
16
+ end
17
+
18
+ class Astronaut < ActiveRecord::Base
19
+ end
20
+
21
+ setup do
22
+ @connection = ActiveRecord::Base.lease_connection
23
+ @connection.create_table "rockets", force: true do |t|
24
+ t.string :name
25
+ end
26
+
27
+ @connection.create_table "astronauts", force: true do |t|
28
+ t.string :name
29
+ t.bigint :secret_id, hidden: true
30
+ end
31
+ end
32
+
33
+ teardown do
34
+ @connection.drop_table "astronauts", if_exists: true
35
+ @connection.drop_table "rockets", if_exists: true
36
+ end
37
+
38
+ # rowid is a special hidden column. CRDB implicitly adds it, so it should
39
+ # not appear in the schema dump.
40
+ def test_rowid_not_in_dump
41
+ output = dump_table_schema "rockets"
42
+ assert_match %r{create_table "rockets", id: false, force: :cascade do |t|"$}, output
43
+ assert_no_match %r{rowid}, output
44
+ end
45
+
46
+ def test_hidden_column
47
+ output = dump_table_schema "astronauts"
48
+ assert_match %r{t.bigint "secret_id", hidden: true$}, output
49
+ end
50
+
51
+ def test_add_hidden_column
52
+ @connection.add_column :rockets, :new_col, :uuid, hidden: true
53
+ output = dump_table_schema "rockets"
54
+ assert_match %r{t.uuid "new_col", hidden: true$}, output
55
+ end
56
+
57
+ # Since 24.2.2, hash sharded indexes add a hidden column to the table.
58
+ # This tests ensure that the user can still drop the index even if they
59
+ # call `#remove_index` with the column name rather than the index name.
60
+ def test_remove_index_with_a_hidden_column
61
+ @connection.execute <<-SQL
62
+ CREATE INDEX hash_idx ON rockets (name) USING HASH WITH (bucket_count=8);
63
+ SQL
64
+ @connection.remove_index :rockets, :name
65
+ assert :ok
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end