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.
Files changed (239) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -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 +39 -21
  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 @@
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_validate_uniqueness_uuid, "This test is only meant to be run with a PostgreSQL adapter"
2
+ exclude :test_validate_case_insensitive_uniqueness, "This tests relies on an implemented pg_cast table, see cockroach/cockroach#47498 for more details"
@@ -0,0 +1,3 @@
1
+ instance_methods.grep(/\Atest_\w+\z/).each do |method_name|
2
+ exclude method_name, "UNLOGGED has no effect in CockroachDB."
3
+ end
@@ -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,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Building < ActiveRecord::Base
4
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This does not have an associated tabled in the cockroach_specific_schema
4
+ class SpatialModel < ActiveRecord::Base
5
+ end
@@ -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