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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bb3247022d4dd7a36aaf82f09bfe1502c9106f570b14e6d09c9cd1076c2b8dc6
4
- data.tar.gz: 0a8deb6f3b4741296899a08c1fc29df3afd0dcaf946538600e4e39e7f5e98170
3
+ metadata.gz: f24596dd159836cb67f7a9f181e32e8370d01420cdb574a96bdf901e802b2669
4
+ data.tar.gz: 126991cc6c0e158ee27386132fc6cc046566720006b020df28cb5495e38d0a08
5
5
  SHA512:
6
- metadata.gz: 48c6242571df5e27fa2bc30f5a699c64bbe4d3f37c47d3ab3deedbc39050470636a664df51c1c47b74e970b6950b995746ee715e44818c91bcd1d832b400bb8f
7
- data.tar.gz: 82169f6e8946a5c6ecbd82001b7fa2e163bc11f739946c184ca4aedbdb95a700e5522fa304ed302050d70e26fb61a65162cfdc8f4be934f96c31c386b8bca5d5
6
+ metadata.gz: ceab189ddbb6c54dbf586c2f3b70c520076c727447304d244924d75029d545fab8c4ebd6864ed00016c8c5cd04476141c64eb1758466cac6762162ce5fbf199e
7
+ data.tar.gz: 1a4fb49e5189772b207e1633c4375943fdee80f3cf8350372d53cf0f1f34a3e7e425a99ae504be585868e2f5565c4a5703bd428692de8d2cf8d0658c42b035d5
data/CHANGELOG.md CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  ## Ongoing
4
4
 
5
+ ## 8.01 - 2025-11-25
6
+ - Add support for Rails 8.1 ([#386](https://github.com/cockroachdb/activerecord-cockroachdb-adapter/pull/386))
7
+
5
8
  ## 8.0.3 - 2025-08-19
6
9
 
7
10
  - Fixed the handling of column comments that end in a single quote ([#382](https://github.com/cockroachdb/activerecord-cockroachdb-adapter/pull/382))
data/CONTRIBUTING.md CHANGED
@@ -61,23 +61,16 @@ Only do it if you know the schema was left in a correct state.
61
61
 
62
62
  ### Run Tests from a Backup
63
63
 
64
- Loading the full test schema every time a test runs can take a while, so for cases where loading the schema sequentially is unimportant, it is possible to use a backup to set up the database. This is significantly faster than the standard method and is provided to run individual tests faster, but should not be used to validate a build.
65
-
66
- First create the template database.
67
-
68
- ```bash
69
- bundle exec rake db:create_test_template
70
- ```
71
-
72
- This will create a template database for the current version (ex. `activerecord_test_template611` for version 6.1.1) and create a `BACKUP` in the `nodelocal://self/activerecord-crdb-adapter/#{activerecord_version}` directory.
73
-
74
- To load from the template, use the `COCKROACH_LOAD_FROM_TEMPLATE` flag.
75
-
76
- ```bash
77
- COCKROACH_LOAD_FROM_TEMPLATE=1 TEST_FILES="test/cases/adapters/postgresql/ddl_test.rb" bundle exec rake test
78
- ```
79
-
80
- And the `activerecord_unittest` database will use the `RESTORE` command to load the schema from the template database.
64
+ Loading the full test schema every time a test runs can take
65
+ a while, so for cases where loading the schema sequentially
66
+ is unimportant, it is possible to use a backup to set up the
67
+ database. This is significantly faster than the standard
68
+ method and is provided to run individual tests faster, but
69
+ should not be used to validate a build.
70
+
71
+ To do so, just set the env variable `COCKROACH_LOAD_FROM_TEMPLATE`.
72
+ First run will generate and cache a template, latter runs will use
73
+ it.
81
74
 
82
75
  # Improvements
83
76
 
data/Gemfile CHANGED
@@ -52,10 +52,11 @@ group :development, :test do
52
52
  gem "msgpack", ">= 1.7.0"
53
53
  gem "mutex_m", "~> 0.2.0"
54
54
 
55
+ gem "tracer"
55
56
  gem "rake"
56
57
  gem "debug"
58
+ gem "minitest-bisect", github: "BuonOmo/minitest-bisect", branch: "main"
57
59
  gem "minitest-excludes", "~> 2.0.1"
58
- gem "minitest-github_action_reporter", github: "BuonOmo/minitest-github_action_reporter", require: "minitest/github_action_reporter_plugin"
59
60
  gem "ostruct", "~> 0.6"
60
61
 
61
62
  # Gems used for tests meta-programming.
data/Rakefile CHANGED
@@ -2,28 +2,9 @@ require "bundler/gem_tasks"
2
2
  require "rake/testtask"
3
3
  require_relative 'test/support/paths_cockroachdb'
4
4
  require_relative 'test/support/rake_helpers'
5
- require_relative 'test/support/template_creator'
6
5
 
7
6
  task default: [:test]
8
7
 
9
- namespace :db do
10
- task "create_test_template" do
11
- ENV['DEBUG_COCKROACHDB_ADAPTER'] = "1"
12
- ENV['COCKROACH_SKIP_LOAD_SCHEMA'] = "1"
13
-
14
- TemplateCreator.connect
15
- require_relative 'test/cases/helper'
16
-
17
- # TODO: look into this more, but for some reason the blob alias
18
- # is not defined while running this task.
19
- ActiveRecord::ConnectionAdapters::CockroachDB::TableDefinition.class_eval do
20
- alias :blob :binary
21
- end
22
-
23
- TemplateCreator.create_test_template
24
- end
25
- end
26
-
27
8
  Rake::TestTask.new do |t|
28
9
  t.libs = ARTest::CockroachDB.test_load_paths
29
10
  t.test_files = RakeHelpers.test_files
@@ -20,33 +20,29 @@ module ActiveRecord
20
20
  class Column < PostgreSQL::Column
21
21
  # most functions taken from activerecord-postgis-adapter spatial_column
22
22
  # https://github.com/rgeo/activerecord-postgis-adapter/blob/master/lib/active_record/connection_adapters/postgis/spatial_column.rb
23
- def initialize(name, default, sql_type_metadata = nil, null = true,
23
+ def initialize(name, cast_type, default, sql_type_metadata = nil, null = true,
24
24
  default_function = nil, collation: nil, comment: nil, identity: nil,
25
- serial: nil, spatial: nil, generated: nil, hidden: nil)
26
- @sql_type_metadata = sql_type_metadata
27
- @geographic = !!(sql_type_metadata.sql_type =~ /geography\(/i)
25
+ serial: nil, generated: nil, hidden: nil)
26
+
27
+ super(name, cast_type, default, sql_type_metadata, null, default_function,
28
+ collation: collation, comment: comment, serial: serial, generated: generated, identity: identity)
29
+
30
+ @geographic = sql_type_metadata.sql_type.match?(/geography\(/i)
28
31
  @hidden = hidden
29
32
 
30
- if spatial
31
- # This case comes from an entry in the geometry_columns table
32
- set_geometric_type_from_name(spatial[:type])
33
- @srid = spatial[:srid].to_i
34
- @has_z = !!spatial[:has_z]
35
- @has_m = !!spatial[:has_m]
36
- elsif @geographic
33
+ if @geographic
37
34
  # Geographic type information is embedded in the SQL type
38
35
  @srid = 4326
39
36
  @has_z = @has_m = false
40
37
  build_from_sql_type(sql_type_metadata.sql_type)
41
- elsif sql_type =~ /geography|geometry|point|linestring|polygon/i
38
+ elsif sql_type.match?(/geography|geometry|point|linestring|polygon/i)
42
39
  build_from_sql_type(sql_type_metadata.sql_type)
43
- elsif sql_type_metadata.sql_type =~ /geography|geometry|point|linestring|polygon/i
40
+ elsif sql_type_metadata.sql_type.match?(/geography|geometry|point|linestring|polygon/i)
44
41
  # A geometry column with no geometry_columns entry.
45
42
  # @geometric_type = geo_type_from_sql_type(sql_type)
46
43
  build_from_sql_type(sql_type_metadata.sql_type)
47
44
  end
48
- super(name, default, sql_type_metadata, null, default_function,
49
- collation: collation, comment: comment, serial: serial, generated: generated, identity: identity)
45
+
50
46
  if spatial? && @srid
51
47
  @limit = { srid: @srid, type: to_type_name(geometric_type) }
52
48
  @limit[:has_z] = true if @has_z
@@ -59,20 +55,18 @@ module ActiveRecord
59
55
  :geometric_type,
60
56
  :has_m,
61
57
  :has_z,
62
- :srid
58
+ :srid,
59
+ :hidden
63
60
 
64
61
  alias geographic? geographic
65
62
  alias has_z? has_z
66
63
  alias has_m? has_m
64
+ alias hidden? hidden
67
65
 
68
66
  def limit
69
67
  spatial? ? @limit : super
70
68
  end
71
69
 
72
- def hidden?
73
- @hidden
74
- end
75
-
76
70
  def spatial?
77
71
  %i[geometry geography].include?(@sql_type_metadata.type)
78
72
  end
@@ -81,15 +75,63 @@ module ActiveRecord
81
75
  default_function == 'unique_rowid()'
82
76
  end
83
77
 
84
- private
78
+ # TODO: add tests (see #390)
79
+ def init_with(coder)
80
+ @geographic = coder["geographic"]
81
+ @geometric_type = coder["geometric_type"]
82
+ @has_m = coder["has_m"]
83
+ @has_z = coder["has_z"]
84
+ @srid = coder["srid"]
85
+ @hidden = coder["hidden"]
86
+ @limit = coder["limit"]
87
+ super
88
+ end
89
+
90
+ # TODO: add tests (see #390)
91
+ def encode_with(coder)
92
+ coder["geographic"] = @geographic
93
+ coder["geometric_type"] = @geometric_type
94
+ coder["has_m"] = @has_m
95
+ coder["has_z"] = @has_z
96
+ coder["srid"] = @srid
97
+ coder["hidden"] = @hidden
98
+ coder["limit"] = @limit
99
+ super
100
+ end
101
+
102
+ # TODO: add tests (see #390)
103
+ def ==(other)
104
+ other.is_a?(Column) &&
105
+ super &&
106
+ other.geographic == geographic &&
107
+ other.geometric_type == geometric_type &&
108
+ other.has_m == has_m &&
109
+ other.has_z == has_z &&
110
+ other.srid == srid &&
111
+ other.hidden == hidden &&
112
+ other.limit == limit
85
113
 
86
- def set_geometric_type_from_name(name)
87
- @geometric_type = RGeo::ActiveRecord.geometric_type_from_name(name) || RGeo::Feature::Geometry
88
114
  end
115
+ alias :eql? :==
116
+
117
+ # TODO: add tests (see #390)
118
+ def hash
119
+ Column.hash ^
120
+ super.hash ^
121
+ geographic.hash ^
122
+ geometric_type.hash ^
123
+ has_m.hash ^
124
+ has_z.hash ^
125
+ srid.hash ^
126
+ hidden.hash ^
127
+ limit.hash
128
+ end
129
+
130
+ private
89
131
 
90
132
  def build_from_sql_type(sql_type)
91
133
  geo_type, @srid, @has_z, @has_m = OID::Spatial.parse_sql_type(sql_type)
92
- set_geometric_type_from_name(geo_type)
134
+ @geometric_type = RGeo::ActiveRecord.geometric_type_from_name(geo_type) || RGeo::Feature::Geometry
93
135
  end
94
136
 
95
137
  def to_type_name(geometric_type)
@@ -22,15 +22,25 @@ module ActiveRecord
22
22
  def insert_fixtures_set(fixture_set, tables_to_delete = [])
23
23
  fixture_inserts = build_fixture_statements(fixture_set)
24
24
  table_deletes = tables_to_delete.map { |table| "DELETE FROM #{quote_table_name(table)}" }
25
- statements = table_deletes + fixture_inserts
25
+ statements = (table_deletes + fixture_inserts).join(";")
26
26
 
27
+ # Since [rails pull request #52428][1], `#execute_batch` does not
28
+ # trigger a cache clear anymore. However, `#insert_fixtures_set`
29
+ # relies on that clear to ensure consistency. In the postgresql
30
+ # adapter, this is ensured by a call to `#execute` rather than
31
+ # `#execute_batch` in `#disable_referential_integrity`. Since
32
+ # we are not always calling `#disable_referential_integrity`,
33
+ # we need to ensure that the cache is cleared when running
34
+ # our statements by calling `#execute` instead of `#execute_batch`.
35
+ #
36
+ # [1]: https://github.com/rails/rails/pull/52428
27
37
  begin # much faster without disabling referential integrity, worth trying.
28
38
  transaction(requires_new: true) do
29
- execute_batch(statements, "Fixtures Load")
39
+ execute(statements, "Fixtures Load")
30
40
  end
31
41
  rescue
32
42
  disable_referential_integrity do
33
- execute_batch(statements, "Fixtures Load")
43
+ execute(statements, "Fixtures Load")
34
44
  end
35
45
  end
36
46
  end
@@ -74,7 +74,7 @@ module ActiveRecord
74
74
  "https://github.com/cockroachdb/activerecord-cockroachdb-adapter/issues/new"
75
75
  end
76
76
 
77
- run_cmd("cockroach", ["sql", "--set", "errexit=false", "--file", filename], "loading")
77
+ run_cmd("cockroach", "sql", "--set", "errexit=false", "--file", filename)
78
78
  end
79
79
 
80
80
  private
@@ -26,9 +26,22 @@ module ActiveRecord
26
26
  # "geometry(Polygon,4326) NOT NULL"
27
27
  # "geometry(Geography,4326)"
28
28
  def initialize(oid, sql_type)
29
- @sql_type = sql_type
30
- @geo_type, @srid, @has_z, @has_m = self.class.parse_sql_type(sql_type)
29
+ super()
30
+ @sql_type = sql_type.freeze
31
+ @factory_attrs = self.class
32
+ .parse_sql_type(sql_type)
33
+ .then { |geo_type, srid, has_z, has_m|
34
+ {
35
+ geo_type: geo_type.underscore.freeze,
36
+ srid: srid.freeze,
37
+ has_z: has_z.freeze,
38
+ has_m: has_m.freeze,
39
+ sql_type: type.to_s.freeze
40
+ }
41
+ }
42
+ .freeze
31
43
  end
44
+ protected attr_reader :sql_type, :factory_attrs
32
45
 
33
46
  # sql_type: geometry, geometry(Point), geometry(Point,4326), ...
34
47
  #
@@ -59,15 +72,8 @@ module ActiveRecord
59
72
  [geo_type, srid, has_z, has_m]
60
73
  end
61
74
 
62
- def spatial_factory
63
- @spatial_factory ||=
64
- RGeo::ActiveRecord::SpatialFactoryStore.instance.factory(
65
- factory_attrs
66
- )
67
- end
68
-
69
75
  def geographic?
70
- @sql_type =~ /geography/
76
+ @sql_type.start_with?("geography")
71
77
  end
72
78
 
73
79
  def spatial?
@@ -92,6 +98,19 @@ module ActiveRecord
92
98
  .generate(geo_value)
93
99
  end
94
100
 
101
+ # TODO: add tests (see #390)
102
+ def ==(other)
103
+ super &&
104
+ @sql_type == other.sql_type &&
105
+ @factory_attrs == other.factory_attrs
106
+ end
107
+ alias eql? ==
108
+
109
+ # TODO: add tests (see #390)
110
+ def hash
111
+ super ^ [@sql_type, @factory_attrs].hash
112
+ end
113
+
95
114
  private
96
115
 
97
116
  def cast_value(value)
@@ -108,7 +127,7 @@ module ActiveRecord
108
127
  end
109
128
 
110
129
  def binary_string?(string)
111
- string[0] == "\x00" || string[0] == "\x01" || string[0, 4] =~ /[0-9a-fA-F]{4}/
130
+ string[0] == "\x00" || string[0] == "\x01" || string[0, 4].match?(/[0-9a-fA-F]{4}/)
112
131
  end
113
132
 
114
133
  def wkt_parser(string)
@@ -119,14 +138,10 @@ module ActiveRecord
119
138
  end
120
139
  end
121
140
 
122
- def factory_attrs
123
- {
124
- geo_type: @geo_type.underscore,
125
- has_m: @has_m,
126
- has_z: @has_z,
127
- srid: @srid,
128
- sql_type: type.to_s
129
- }
141
+ def spatial_factory
142
+ RGeo::ActiveRecord::SpatialFactoryStore.instance.factory(
143
+ factory_attrs
144
+ )
130
145
  end
131
146
  end
132
147
  end
@@ -34,7 +34,8 @@ module ActiveRecord
34
34
  # but when creating objects, using RGeo features is more convenient than
35
35
  # converting to WKB, so this does it automatically.
36
36
  def quote(value)
37
- if value.is_a?(Numeric)
37
+ case value
38
+ when Numeric
38
39
  # NOTE: The fact that integers are quoted is important and helps
39
40
  # mitigate a potential vulnerability.
40
41
  #
@@ -42,9 +43,9 @@ module ActiveRecord
42
43
  # - https://nvd.nist.gov/vuln/detail/CVE-2022-44566
43
44
  # - https://github.com/cockroachdb/activerecord-cockroachdb-adapter/pull/280#discussion_r1288692977
44
45
  "'#{quote_string(value.to_s)}'"
45
- elsif RGeo::Feature::Geometry.check_type(value)
46
+ when RGeo::Feature::Geometry
46
47
  "'#{RGeo::WKRep::WKBGenerator.new(hex_format: true, type_format: :ewkb, emit_ewkb_srid: true).generate(value)}'"
47
- elsif value.is_a?(RGeo::Cartesian::BoundingBox)
48
+ when RGeo::Cartesian::BoundingBox
48
49
  "'#{value.min_x},#{value.min_y},#{value.max_x},#{value.max_y}'::box"
49
50
  else
50
51
  super
@@ -58,6 +59,21 @@ module ActiveRecord
58
59
  # This is tested by `BasicsTest#test_default_in_local_time`.
59
60
  super + value.strftime("%z")
60
61
  end
62
+
63
+ # NOTE: This method should be private in future rails versions.
64
+ # Hence we should also make it private then.
65
+ #
66
+ # See https://github.com/rails/rails/blob/v8.1.1/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb#L190
67
+ def lookup_cast_type(sql_type)
68
+ type_map.lookup(
69
+ # oid
70
+ query_value("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").to_i,
71
+ # fmod, not needed.
72
+ nil,
73
+ # details needed for `..::CockroachDB::OID::Spatial` (e.g. `geometry(point,3857)`)
74
+ sql_type
75
+ )
76
+ end
61
77
  end
62
78
  end
63
79
  end
@@ -34,8 +34,47 @@ module ActiveRecord
34
34
  end
35
35
 
36
36
  def disable_referential_integrity
37
- foreign_keys = all_foreign_keys
37
+ if transaction_open? && query_value("SHOW autocommit_before_ddl") == "off"
38
+ begin
39
+ yield
40
+ rescue ActiveRecord::InvalidForeignKey => e
41
+ warn <<-WARNING
42
+ WARNING: Rails was not able to disable referential integrity.
43
+
44
+ This is due to CockroachDB's need of committing transactions
45
+ before a schema change occurs. To bypass this, you can set
46
+ `autocommit_before_ddl: "on"` in your database configuration.
47
+ WARNING
48
+ raise e
49
+ end
50
+ else
51
+ foreign_keys = all_foreign_keys
52
+
53
+ remove_foreign_keys(foreign_keys)
54
+
55
+ # Prefixes and suffixes are added in add_foreign_key
56
+ # in AR7+ so we need to temporarily disable them here,
57
+ # otherwise prefixes/suffixes will be erroneously added.
58
+ old_prefix = ActiveRecord::Base.table_name_prefix
59
+ old_suffix = ActiveRecord::Base.table_name_suffix
60
+
61
+ begin
62
+ yield
63
+ ensure
64
+ ActiveRecord::Base.table_name_prefix = ""
65
+ ActiveRecord::Base.table_name_suffix = ""
66
+
67
+ add_foreign_keys(foreign_keys) # Never raises.
68
+
69
+ ActiveRecord::Base.table_name_prefix = old_prefix if defined?(old_prefix)
70
+ ActiveRecord::Base.table_name_suffix = old_suffix if defined?(old_suffix)
71
+ end
72
+ end
73
+ end
38
74
 
75
+ private
76
+
77
+ def remove_foreign_keys(foreign_keys)
39
78
  statements = foreign_keys.map do |foreign_key|
40
79
  # We do not use the `#remove_foreign_key` method here because it
41
80
  # checks for foreign keys existance in the schema cache. This method
@@ -46,47 +85,30 @@ module ActiveRecord
46
85
  schema_creation.accept(at)
47
86
  end
48
87
  execute_batch(statements, "Disable referential integrity -> remove foreign keys")
88
+ end
49
89
 
50
- yield
51
-
52
- # Prefixes and suffixes are added in add_foreign_key
53
- # in AR7+ so we need to temporarily disable them here,
54
- # otherwise prefixes/suffixes will be erroneously added.
55
- old_prefix = ActiveRecord::Base.table_name_prefix
56
- old_suffix = ActiveRecord::Base.table_name_suffix
57
-
58
- ActiveRecord::Base.table_name_prefix = ""
59
- ActiveRecord::Base.table_name_suffix = ""
60
-
61
- begin
62
- # Avoid having PG:DuplicateObject error if a test is ran in transaction.
63
- # TODO: verify that there is no cache issue related to running this (e.g: fk
64
- # still in cache but not in db)
65
- #
66
- # We avoid using `foreign_key_exists?` here because it checks the schema cache
67
- # for every key. This method is performance critical for the test suite, hence
68
- # we use the `#all_foreign_keys` method that only make one query to the database.
69
- already_inserted_foreign_keys = all_foreign_keys
70
- statements = foreign_keys.map do |foreign_key|
71
- next if already_inserted_foreign_keys.any? { |fk| fk.from_table == foreign_key.from_table && fk.options[:name] == foreign_key.options[:name] }
72
-
73
- options = foreign_key_options(foreign_key.from_table, foreign_key.to_table, foreign_key.options)
74
- at = create_alter_table foreign_key.from_table
75
- at.add_foreign_key foreign_key.to_table, options
76
-
77
- schema_creation.accept(at)
78
- end
79
- execute_batch(statements.compact, "Disable referential integrity -> add foreign keys")
80
- ensure
81
- ActiveRecord::Base.table_name_prefix = old_prefix
82
- ActiveRecord::Base.table_name_suffix = old_suffix
90
+ # NOTE: This method should never raise, otherwise we risk polluting table name
91
+ # prefixes and suffixes. The good thing is: if this happens, tests will crash
92
+ # hard, no way we miss it.
93
+ def add_foreign_keys(foreign_keys)
94
+ # We avoid using `foreign_key_exists?` here because it checks the schema cache
95
+ # for every key. This method is performance critical for the test suite, hence
96
+ # we use the `#all_foreign_keys` method that only make one query to the database.
97
+ already_inserted_foreign_keys = all_foreign_keys
98
+ statements = foreign_keys.map do |foreign_key|
99
+ next if already_inserted_foreign_keys.any? { |fk| fk.from_table == foreign_key.from_table && fk.options[:name] == foreign_key.options[:name] }
100
+
101
+ options = foreign_key_options(foreign_key.from_table, foreign_key.to_table, foreign_key.options)
102
+ at = create_alter_table foreign_key.from_table
103
+ at.add_foreign_key foreign_key.to_table, options
104
+
105
+ schema_creation.accept(at)
83
106
  end
107
+ execute_batch(statements.compact, "Disable referential integrity -> add foreign keys")
84
108
  end
85
109
 
86
- private
87
-
88
- # Copy/paste of the `#foreign_keys(table)` method adapted to return every single
89
- # foreign key in the database.
110
+ # NOTE: Copy/paste of the `#foreign_keys(table)` method adapted
111
+ # to return every single foreign key in the database.
90
112
  def all_foreign_keys
91
113
  fk_info = internal_exec_query(<<~SQL, "SCHEMA")
92
114
  SELECT CASE
@@ -99,14 +121,30 @@ module ActiveRecord
99
121
  THEN ''
100
122
  ELSE n2.nspname || '.'
101
123
  END || t2.relname AS to_table,
102
- a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete, c.convalidated AS valid, c.condeferrable AS deferrable, c.condeferred AS deferred,
103
- c.conkey, c.confkey, c.conrelid, c.confrelid
124
+ c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete, c.convalidated AS valid, c.condeferrable AS deferrable, c.condeferred AS deferred, c.conrelid, c.confrelid,
125
+ (
126
+ SELECT array_agg(a.attname ORDER BY idx)
127
+ FROM (
128
+ SELECT idx, c.conkey[idx] AS conkey_elem
129
+ FROM generate_subscripts(c.conkey, 1) AS idx
130
+ ) indexed_conkeys
131
+ JOIN pg_attribute a ON a.attrelid = t1.oid
132
+ AND a.attnum = indexed_conkeys.conkey_elem
133
+ AND NOT a.attishidden
134
+ ) AS conkey_names,
135
+ (
136
+ SELECT array_agg(a.attname ORDER BY idx)
137
+ FROM (
138
+ SELECT idx, c.confkey[idx] AS confkey_elem
139
+ FROM generate_subscripts(c.confkey, 1) AS idx
140
+ ) indexed_confkeys
141
+ JOIN pg_attribute a ON a.attrelid = t2.oid
142
+ AND a.attnum = indexed_confkeys.confkey_elem
143
+ AND NOT a.attishidden
144
+ ) AS confkey_names
104
145
  FROM pg_constraint c
105
146
  JOIN pg_class t1 ON c.conrelid = t1.oid
106
147
  JOIN pg_class t2 ON c.confrelid = t2.oid
107
- JOIN pg_attribute a1 ON a1.attnum = c.conkey[1] AND a1.attrelid = t1.oid
108
- JOIN pg_attribute a2 ON a2.attnum = c.confkey[1] AND a2.attrelid = t2.oid
109
- JOIN pg_namespace t3 ON c.connamespace = t3.oid
110
148
  JOIN pg_namespace n1 ON t1.relnamespace = n1.oid
111
149
  JOIN pg_namespace n2 ON t2.relnamespace = n2.oid
112
150
  WHERE c.contype = 'f'
@@ -116,22 +154,16 @@ module ActiveRecord
116
154
  fk_info.map do |row|
117
155
  from_table = PostgreSQL::Utils.unquote_identifier(row["from_table"])
118
156
  to_table = PostgreSQL::Utils.unquote_identifier(row["to_table"])
119
- conkey = row["conkey"].scan(/\d+/).map(&:to_i)
120
- confkey = row["confkey"].scan(/\d+/).map(&:to_i)
121
-
122
- if conkey.size > 1
123
- column = column_names_from_column_numbers(row["conrelid"], conkey)
124
- primary_key = column_names_from_column_numbers(row["confrelid"], confkey)
125
- else
126
- column = PostgreSQL::Utils.unquote_identifier(row["column"])
127
- primary_key = row["primary_key"]
128
- end
157
+
158
+ column = decode_string_array(row["conkey_names"])
159
+ primary_key = decode_string_array(row["confkey_names"])
129
160
 
130
161
  options = {
131
- column: column,
162
+ column: column.size == 1 ? column.first : column,
132
163
  name: row["name"],
133
- primary_key: primary_key
164
+ primary_key: primary_key.size == 1 ? primary_key.first : primary_key
134
165
  }
166
+
135
167
  options[:on_delete] = extract_foreign_key_action(row["on_delete"])
136
168
  options[:on_update] = extract_foreign_key_action(row["on_update"])
137
169
  options[:deferrable] = extract_constraint_deferrable(row["deferrable"], row["deferred"])