activerecord-spanner-adapter 0.3.0 → 0.5.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 (264) hide show
  1. checksums.yaml +5 -5
  2. data/.github/CODEOWNERS +7 -0
  3. data/.github/sync-repo-settings.yaml +16 -0
  4. data/.github/workflows/acceptance-tests-on-emulator.yaml +45 -0
  5. data/.github/workflows/acceptance-tests-on-production.yaml +36 -0
  6. data/.github/workflows/ci.yaml +33 -0
  7. data/.github/workflows/nightly-acceptance-tests-on-emulator.yaml +52 -0
  8. data/.github/workflows/nightly-acceptance-tests-on-production.yaml +35 -0
  9. data/.github/workflows/nightly-unit-tests.yaml +40 -0
  10. data/.github/workflows/release-please-label.yml +25 -0
  11. data/.github/workflows/release-please.yml +39 -0
  12. data/.github/workflows/rubocop.yaml +31 -0
  13. data/.gitignore +67 -5
  14. data/.kokoro/populate-secrets.sh +77 -0
  15. data/.kokoro/release.cfg +33 -0
  16. data/.kokoro/release.sh +15 -0
  17. data/.kokoro/trampoline_v2.sh +489 -0
  18. data/.rubocop.yml +46 -0
  19. data/.toys/release.rb +18 -0
  20. data/.trampolinerc +48 -0
  21. data/.yardopts +11 -0
  22. data/CHANGELOG.md +26 -0
  23. data/CODE_OF_CONDUCT.md +40 -0
  24. data/CONTRIBUTING.md +79 -0
  25. data/Gemfile +9 -4
  26. data/LICENSE +6 -6
  27. data/README.md +67 -30
  28. data/Rakefile +79 -3
  29. data/SECURITY.md +7 -0
  30. data/acceptance/cases/associations/has_many_associations_test.rb +119 -0
  31. data/acceptance/cases/associations/has_many_through_associations_test.rb +63 -0
  32. data/acceptance/cases/associations/has_one_associations_test.rb +79 -0
  33. data/acceptance/cases/associations/has_one_through_associations_test.rb +98 -0
  34. data/acceptance/cases/interleaved_associations/has_many_associations_using_interleaved_test.rb +211 -0
  35. data/acceptance/cases/migration/change_schema_test.rb +433 -0
  36. data/acceptance/cases/migration/change_table_test.rb +115 -0
  37. data/acceptance/cases/migration/column_attributes_test.rb +122 -0
  38. data/acceptance/cases/migration/column_positioning_test.rb +48 -0
  39. data/acceptance/cases/migration/columns_test.rb +201 -0
  40. data/acceptance/cases/migration/command_recorder_test.rb +406 -0
  41. data/acceptance/cases/migration/create_join_table_test.rb +216 -0
  42. data/acceptance/cases/migration/ddl_batching_test.rb +80 -0
  43. data/acceptance/cases/migration/foreign_key_test.rb +297 -0
  44. data/acceptance/cases/migration/index_test.rb +211 -0
  45. data/acceptance/cases/migration/references_foreign_key_test.rb +259 -0
  46. data/acceptance/cases/migration/references_index_test.rb +135 -0
  47. data/acceptance/cases/migration/references_statements_test.rb +166 -0
  48. data/acceptance/cases/migration/rename_column_test.rb +96 -0
  49. data/acceptance/cases/models/calculation_query_test.rb +128 -0
  50. data/acceptance/cases/models/generated_column_test.rb +126 -0
  51. data/acceptance/cases/models/mutation_test.rb +122 -0
  52. data/acceptance/cases/models/query_test.rb +147 -0
  53. data/acceptance/cases/sessions/session_not_found_test.rb +121 -0
  54. data/acceptance/cases/transactions/optimistic_locking_test.rb +141 -0
  55. data/acceptance/cases/transactions/read_only_transactions_test.rb +67 -0
  56. data/acceptance/cases/transactions/read_write_transactions_test.rb +248 -0
  57. data/acceptance/cases/type/all_types_test.rb +152 -0
  58. data/acceptance/cases/type/binary_test.rb +59 -0
  59. data/acceptance/cases/type/boolean_test.rb +31 -0
  60. data/acceptance/cases/type/date_test.rb +32 -0
  61. data/acceptance/cases/type/date_time_test.rb +30 -0
  62. data/acceptance/cases/type/float_test.rb +27 -0
  63. data/acceptance/cases/type/integer_test.rb +44 -0
  64. data/acceptance/cases/type/numeric_test.rb +27 -0
  65. data/acceptance/cases/type/string_test.rb +79 -0
  66. data/acceptance/cases/type/text_test.rb +30 -0
  67. data/acceptance/cases/type/time_test.rb +87 -0
  68. data/acceptance/models/account.rb +13 -0
  69. data/acceptance/models/address.rb +9 -0
  70. data/acceptance/models/album.rb +12 -0
  71. data/acceptance/models/all_types.rb +8 -0
  72. data/acceptance/models/author.rb +11 -0
  73. data/acceptance/models/club.rb +12 -0
  74. data/acceptance/models/comment.rb +9 -0
  75. data/acceptance/models/customer.rb +9 -0
  76. data/acceptance/models/department.rb +9 -0
  77. data/acceptance/models/firm.rb +10 -0
  78. data/acceptance/models/member.rb +13 -0
  79. data/acceptance/models/member_type.rb +9 -0
  80. data/acceptance/models/membership.rb +10 -0
  81. data/acceptance/models/organization.rb +9 -0
  82. data/acceptance/models/post.rb +10 -0
  83. data/acceptance/models/singer.rb +10 -0
  84. data/acceptance/models/track.rb +20 -0
  85. data/acceptance/models/transaction.rb +9 -0
  86. data/acceptance/schema/schema.rb +143 -0
  87. data/acceptance/test_helper.rb +260 -0
  88. data/activerecord-spanner-adapter.gemspec +32 -17
  89. data/assets/solidus-db.png +0 -0
  90. data/benchmarks/README.md +17 -0
  91. data/benchmarks/Rakefile +14 -0
  92. data/benchmarks/application.rb +308 -0
  93. data/benchmarks/config/database.yml +8 -0
  94. data/benchmarks/config/environment.rb +12 -0
  95. data/benchmarks/db/migrate/01_create_tables.rb +25 -0
  96. data/benchmarks/db/schema.rb +29 -0
  97. data/benchmarks/models/album.rb +9 -0
  98. data/benchmarks/models/singer.rb +9 -0
  99. data/bin/console +6 -7
  100. data/examples/rails/README.md +262 -0
  101. data/examples/snippets/README.md +29 -0
  102. data/examples/snippets/Rakefile +57 -0
  103. data/examples/snippets/array-data-type/README.md +45 -0
  104. data/examples/snippets/array-data-type/Rakefile +13 -0
  105. data/examples/snippets/array-data-type/application.rb +45 -0
  106. data/examples/snippets/array-data-type/config/database.yml +8 -0
  107. data/examples/snippets/array-data-type/db/migrate/01_create_tables.rb +24 -0
  108. data/examples/snippets/array-data-type/db/schema.rb +26 -0
  109. data/examples/snippets/array-data-type/db/seeds.rb +5 -0
  110. data/examples/snippets/array-data-type/models/entity_with_array_types.rb +18 -0
  111. data/examples/snippets/bin/create_emulator_instance.rb +18 -0
  112. data/examples/snippets/bulk-insert/README.md +21 -0
  113. data/examples/snippets/bulk-insert/Rakefile +13 -0
  114. data/examples/snippets/bulk-insert/application.rb +64 -0
  115. data/examples/snippets/bulk-insert/config/database.yml +8 -0
  116. data/examples/snippets/bulk-insert/db/migrate/01_create_tables.rb +21 -0
  117. data/examples/snippets/bulk-insert/db/schema.rb +26 -0
  118. data/examples/snippets/bulk-insert/db/seeds.rb +5 -0
  119. data/examples/snippets/bulk-insert/models/album.rb +9 -0
  120. data/examples/snippets/bulk-insert/models/singer.rb +9 -0
  121. data/examples/snippets/commit-timestamp/README.md +18 -0
  122. data/examples/snippets/commit-timestamp/Rakefile +13 -0
  123. data/examples/snippets/commit-timestamp/application.rb +53 -0
  124. data/examples/snippets/commit-timestamp/config/database.yml +8 -0
  125. data/examples/snippets/commit-timestamp/db/migrate/01_create_tables.rb +26 -0
  126. data/examples/snippets/commit-timestamp/db/schema.rb +29 -0
  127. data/examples/snippets/commit-timestamp/db/seeds.rb +5 -0
  128. data/examples/snippets/commit-timestamp/models/album.rb +9 -0
  129. data/examples/snippets/commit-timestamp/models/singer.rb +9 -0
  130. data/examples/snippets/config/environment.rb +21 -0
  131. data/examples/snippets/create-records/README.md +12 -0
  132. data/examples/snippets/create-records/Rakefile +13 -0
  133. data/examples/snippets/create-records/application.rb +42 -0
  134. data/examples/snippets/create-records/config/database.yml +8 -0
  135. data/examples/snippets/create-records/db/migrate/01_create_tables.rb +21 -0
  136. data/examples/snippets/create-records/db/schema.rb +26 -0
  137. data/examples/snippets/create-records/db/seeds.rb +5 -0
  138. data/examples/snippets/create-records/models/album.rb +9 -0
  139. data/examples/snippets/create-records/models/singer.rb +9 -0
  140. data/examples/snippets/date-data-type/README.md +19 -0
  141. data/examples/snippets/date-data-type/Rakefile +13 -0
  142. data/examples/snippets/date-data-type/application.rb +35 -0
  143. data/examples/snippets/date-data-type/config/database.yml +8 -0
  144. data/examples/snippets/date-data-type/db/migrate/01_create_tables.rb +20 -0
  145. data/examples/snippets/date-data-type/db/schema.rb +21 -0
  146. data/examples/snippets/date-data-type/db/seeds.rb +16 -0
  147. data/examples/snippets/date-data-type/models/singer.rb +8 -0
  148. data/examples/snippets/generated-column/README.md +41 -0
  149. data/examples/snippets/generated-column/Rakefile +13 -0
  150. data/examples/snippets/generated-column/application.rb +37 -0
  151. data/examples/snippets/generated-column/config/database.yml +8 -0
  152. data/examples/snippets/generated-column/db/migrate/01_create_tables.rb +23 -0
  153. data/examples/snippets/generated-column/db/schema.rb +21 -0
  154. data/examples/snippets/generated-column/db/seeds.rb +18 -0
  155. data/examples/snippets/generated-column/models/singer.rb +8 -0
  156. data/examples/snippets/interleaved-tables/README.md +152 -0
  157. data/examples/snippets/interleaved-tables/Rakefile +13 -0
  158. data/examples/snippets/interleaved-tables/application.rb +109 -0
  159. data/examples/snippets/interleaved-tables/config/database.yml +8 -0
  160. data/examples/snippets/interleaved-tables/db/migrate/01_create_tables.rb +44 -0
  161. data/examples/snippets/interleaved-tables/db/schema.rb +32 -0
  162. data/examples/snippets/interleaved-tables/db/seeds.rb +40 -0
  163. data/examples/snippets/interleaved-tables/models/album.rb +15 -0
  164. data/examples/snippets/interleaved-tables/models/singer.rb +20 -0
  165. data/examples/snippets/interleaved-tables/models/track.rb +25 -0
  166. data/examples/snippets/migrations/README.md +43 -0
  167. data/examples/snippets/migrations/Rakefile +13 -0
  168. data/examples/snippets/migrations/application.rb +26 -0
  169. data/examples/snippets/migrations/config/database.yml +8 -0
  170. data/examples/snippets/migrations/db/migrate/01_create_tables.rb +28 -0
  171. data/examples/snippets/migrations/db/schema.rb +33 -0
  172. data/examples/snippets/migrations/db/seeds.rb +5 -0
  173. data/examples/snippets/migrations/models/album.rb +10 -0
  174. data/examples/snippets/migrations/models/singer.rb +10 -0
  175. data/examples/snippets/migrations/models/track.rb +9 -0
  176. data/examples/snippets/mutations/README.md +34 -0
  177. data/examples/snippets/mutations/Rakefile +13 -0
  178. data/examples/snippets/mutations/application.rb +47 -0
  179. data/examples/snippets/mutations/config/database.yml +8 -0
  180. data/examples/snippets/mutations/db/migrate/01_create_tables.rb +22 -0
  181. data/examples/snippets/mutations/db/schema.rb +27 -0
  182. data/examples/snippets/mutations/db/seeds.rb +25 -0
  183. data/examples/snippets/mutations/models/album.rb +9 -0
  184. data/examples/snippets/mutations/models/singer.rb +9 -0
  185. data/examples/snippets/optimistic-locking/README.md +12 -0
  186. data/examples/snippets/optimistic-locking/Rakefile +13 -0
  187. data/examples/snippets/optimistic-locking/application.rb +48 -0
  188. data/examples/snippets/optimistic-locking/config/database.yml +8 -0
  189. data/examples/snippets/optimistic-locking/db/migrate/01_create_tables.rb +26 -0
  190. data/examples/snippets/optimistic-locking/db/schema.rb +29 -0
  191. data/examples/snippets/optimistic-locking/db/seeds.rb +25 -0
  192. data/examples/snippets/optimistic-locking/models/album.rb +9 -0
  193. data/examples/snippets/optimistic-locking/models/singer.rb +9 -0
  194. data/examples/snippets/quickstart/README.md +26 -0
  195. data/examples/snippets/quickstart/Rakefile +13 -0
  196. data/examples/snippets/quickstart/application.rb +51 -0
  197. data/examples/snippets/quickstart/config/database.yml +8 -0
  198. data/examples/snippets/quickstart/db/migrate/01_create_tables.rb +21 -0
  199. data/examples/snippets/quickstart/db/schema.rb +26 -0
  200. data/examples/snippets/quickstart/db/seeds.rb +24 -0
  201. data/examples/snippets/quickstart/models/album.rb +9 -0
  202. data/examples/snippets/quickstart/models/singer.rb +9 -0
  203. data/examples/snippets/read-only-transactions/README.md +13 -0
  204. data/examples/snippets/read-only-transactions/Rakefile +13 -0
  205. data/examples/snippets/read-only-transactions/application.rb +49 -0
  206. data/examples/snippets/read-only-transactions/config/database.yml +8 -0
  207. data/examples/snippets/read-only-transactions/db/migrate/01_create_tables.rb +21 -0
  208. data/examples/snippets/read-only-transactions/db/schema.rb +26 -0
  209. data/examples/snippets/read-only-transactions/db/seeds.rb +24 -0
  210. data/examples/snippets/read-only-transactions/models/album.rb +9 -0
  211. data/examples/snippets/read-only-transactions/models/singer.rb +9 -0
  212. data/examples/snippets/read-write-transactions/README.md +12 -0
  213. data/examples/snippets/read-write-transactions/Rakefile +13 -0
  214. data/examples/snippets/read-write-transactions/application.rb +39 -0
  215. data/examples/snippets/read-write-transactions/config/database.yml +8 -0
  216. data/examples/snippets/read-write-transactions/db/migrate/01_create_tables.rb +22 -0
  217. data/examples/snippets/read-write-transactions/db/schema.rb +27 -0
  218. data/examples/snippets/read-write-transactions/db/seeds.rb +25 -0
  219. data/examples/snippets/read-write-transactions/models/album.rb +9 -0
  220. data/examples/snippets/read-write-transactions/models/singer.rb +9 -0
  221. data/examples/snippets/timestamp-data-type/README.md +17 -0
  222. data/examples/snippets/timestamp-data-type/Rakefile +13 -0
  223. data/examples/snippets/timestamp-data-type/application.rb +42 -0
  224. data/examples/snippets/timestamp-data-type/config/database.yml +8 -0
  225. data/examples/snippets/timestamp-data-type/db/migrate/01_create_tables.rb +21 -0
  226. data/examples/snippets/timestamp-data-type/db/schema.rb +21 -0
  227. data/examples/snippets/timestamp-data-type/db/seeds.rb +6 -0
  228. data/examples/snippets/timestamp-data-type/models/meeting.rb +19 -0
  229. data/examples/solidus/README.md +172 -0
  230. data/lib/active_record/connection_adapters/spanner/database_statements.rb +224 -269
  231. data/lib/active_record/connection_adapters/spanner/quoting.rb +42 -50
  232. data/lib/active_record/connection_adapters/spanner/schema_cache.rb +43 -0
  233. data/lib/active_record/connection_adapters/spanner/schema_creation.rb +125 -9
  234. data/lib/active_record/connection_adapters/spanner/schema_definitions.rb +122 -0
  235. data/lib/active_record/connection_adapters/spanner/schema_dumper.rb +19 -0
  236. data/lib/active_record/connection_adapters/spanner/schema_statements.rb +553 -139
  237. data/lib/active_record/connection_adapters/spanner/type_metadata.rb +37 -0
  238. data/lib/active_record/connection_adapters/spanner_adapter.rb +182 -78
  239. data/lib/active_record/tasks/spanner_database_tasks.rb +74 -0
  240. data/lib/active_record/type/spanner/array.rb +32 -0
  241. data/lib/active_record/type/spanner/bytes.rb +26 -0
  242. data/lib/active_record/type/spanner/spanner_active_record_converter.rb +32 -0
  243. data/lib/active_record/type/spanner/time.rb +37 -0
  244. data/lib/activerecord-spanner-adapter.rb +23 -0
  245. data/lib/activerecord_spanner_adapter/base.rb +217 -0
  246. data/lib/activerecord_spanner_adapter/connection.rb +324 -0
  247. data/lib/activerecord_spanner_adapter/errors.rb +13 -0
  248. data/lib/activerecord_spanner_adapter/foreign_key.rb +29 -0
  249. data/lib/activerecord_spanner_adapter/index/column.rb +38 -0
  250. data/lib/activerecord_spanner_adapter/index.rb +80 -0
  251. data/lib/activerecord_spanner_adapter/information_schema.rb +261 -0
  252. data/lib/activerecord_spanner_adapter/primary_key.rb +31 -0
  253. data/lib/activerecord_spanner_adapter/table/column.rb +59 -0
  254. data/lib/activerecord_spanner_adapter/table.rb +61 -0
  255. data/lib/activerecord_spanner_adapter/transaction.rb +113 -0
  256. data/lib/activerecord_spanner_adapter/version.rb +9 -0
  257. data/lib/arel/visitors/spanner.rb +35 -0
  258. data/lib/spanner_client_ext.rb +82 -0
  259. data/renovate.json +5 -0
  260. metadata +387 -34
  261. data/.travis.yml +0 -5
  262. data/lib/active_record/connection_adapters/spanner/client.rb +0 -190
  263. data/lib/active_record/connection_adapters/spanner.rb +0 -10
  264. data/lib/activerecord-spanner-adapter/version.rb +0 -3
@@ -0,0 +1,11 @@
1
+ # Copyright 2020 Google LLC
2
+ #
3
+ # Use of this source code is governed by an MIT-style
4
+ # license that can be found in the LICENSE file or at
5
+ # https://opensource.org/licenses/MIT.
6
+
7
+ class Author < ActiveRecord::Base
8
+ has_many :posts
9
+ has_many :comments, through: :posts
10
+ belongs_to :organization
11
+ end
@@ -0,0 +1,12 @@
1
+ # Copyright 2020 Google LLC
2
+ #
3
+ # Use of this source code is governed by an MIT-style
4
+ # license that can be found in the LICENSE file or at
5
+ # https://opensource.org/licenses/MIT.
6
+
7
+ class Club < ActiveRecord::Base
8
+ has_many :memberships
9
+ has_many :members, through: :memberships
10
+ has_many :favourites, -> { where(memberships: { favourite: true }) },
11
+ through: :memberships, source: :member
12
+ end
@@ -0,0 +1,9 @@
1
+ # Copyright 2020 Google LLC
2
+ #
3
+ # Use of this source code is governed by an MIT-style
4
+ # license that can be found in the LICENSE file or at
5
+ # https://opensource.org/licenses/MIT.
6
+
7
+ class Comment < ActiveRecord::Base
8
+ belongs_to :post, counter_cache: true
9
+ end
@@ -0,0 +1,9 @@
1
+ # Copyright 2020 Google LLC
2
+ #
3
+ # Use of this source code is governed by an MIT-style
4
+ # license that can be found in the LICENSE file or at
5
+ # https://opensource.org/licenses/MIT.
6
+
7
+ class Customer < ActiveRecord::Base
8
+ has_many :accounts
9
+ end
@@ -0,0 +1,9 @@
1
+ # Copyright 2020 Google LLC
2
+ #
3
+ # Use of this source code is governed by an MIT-style
4
+ # license that can be found in the LICENSE file or at
5
+ # https://opensource.org/licenses/MIT.
6
+
7
+ class Department < ActiveRecord::Base
8
+ belongs_to :resource, polymorphic: true
9
+ end
@@ -0,0 +1,10 @@
1
+ # Copyright 2020 Google LLC
2
+ #
3
+ # Use of this source code is governed by an MIT-style
4
+ # license that can be found in the LICENSE file or at
5
+ # https://opensource.org/licenses/MIT.
6
+
7
+ class Firm < ActiveRecord::Base
8
+ has_one :account
9
+ has_many :departments, as: :resource
10
+ end
@@ -0,0 +1,13 @@
1
+ # Copyright 2020 Google LLC
2
+ #
3
+ # Use of this source code is governed by an MIT-style
4
+ # license that can be found in the LICENSE file or at
5
+ # https://opensource.org/licenses/MIT.
6
+
7
+ class Member < ActiveRecord::Base
8
+ has_one :membership
9
+ has_one :club, through: :membership
10
+ has_one :favourite_club, -> { where "memberships.favourite = ?", true },
11
+ through: :membership, source: :club
12
+ belongs_to :member_type
13
+ end
@@ -0,0 +1,9 @@
1
+ # Copyright 2020 Google LLC
2
+ #
3
+ # Use of this source code is governed by an MIT-style
4
+ # license that can be found in the LICENSE file or at
5
+ # https://opensource.org/licenses/MIT.
6
+
7
+ class MemberType < ActiveRecord::Base
8
+ has_many :members
9
+ end
@@ -0,0 +1,10 @@
1
+ # Copyright 2020 Google LLC
2
+ #
3
+ # Use of this source code is governed by an MIT-style
4
+ # license that can be found in the LICENSE file or at
5
+ # https://opensource.org/licenses/MIT.
6
+
7
+ class Membership < ActiveRecord::Base
8
+ belongs_to :member
9
+ belongs_to :club
10
+ end
@@ -0,0 +1,9 @@
1
+ # Copyright 2020 Google LLC
2
+ #
3
+ # Use of this source code is governed by an MIT-style
4
+ # license that can be found in the LICENSE file or at
5
+ # https://opensource.org/licenses/MIT.
6
+
7
+ class Organization < ActiveRecord::Base
8
+ has_many :authors, dependent: :destroy
9
+ end
@@ -0,0 +1,10 @@
1
+ # Copyright 2020 Google LLC
2
+ #
3
+ # Use of this source code is governed by an MIT-style
4
+ # license that can be found in the LICENSE file or at
5
+ # https://opensource.org/licenses/MIT.
6
+
7
+ class Post < ActiveRecord::Base
8
+ belongs_to :author
9
+ has_many :comments
10
+ end
@@ -0,0 +1,10 @@
1
+ # Copyright 2021 Google LLC
2
+ #
3
+ # Use of this source code is governed by an MIT-style
4
+ # license that can be found in the LICENSE file or at
5
+ # https://opensource.org/licenses/MIT.
6
+
7
+ class Singer < ActiveRecord::Base
8
+ has_many :albums, foreign_key: "singerid", dependent: :delete_all
9
+ has_many :tracks, foreign_key: "singerid"
10
+ end
@@ -0,0 +1,20 @@
1
+ # Copyright 2021 Google LLC
2
+ #
3
+ # Use of this source code is governed by an MIT-style
4
+ # license that can be found in the LICENSE file or at
5
+ # https://opensource.org/licenses/MIT.
6
+
7
+ class Track < ActiveRecord::Base
8
+ belongs_to :album, foreign_key: "albumid"
9
+ belongs_to :singer, foreign_key: "singerid", counter_cache: true
10
+
11
+ def initialize attributes = nil
12
+ super
13
+ self.singer ||= self.album&.singer
14
+ end
15
+
16
+ def album=value
17
+ super
18
+ self.singer = value&.singer
19
+ end
20
+ end
@@ -0,0 +1,9 @@
1
+ # Copyright 2020 Google LLC
2
+ #
3
+ # Use of this source code is governed by an MIT-style
4
+ # license that can be found in the LICENSE file or at
5
+ # https://opensource.org/licenses/MIT.
6
+
7
+ class Transaction < ActiveRecord::Base
8
+ belongs_to :account, counter_cache: true
9
+ end
@@ -0,0 +1,143 @@
1
+ # Copyright 2020 Google LLC
2
+ #
3
+ # Use of this source code is governed by an MIT-style
4
+ # license that can be found in the LICENSE file or at
5
+ # https://opensource.org/licenses/MIT.
6
+
7
+ # frozen_string_literal: true
8
+
9
+ ActiveRecord::Schema.define do
10
+ ActiveRecord::Base.connection.ddl_batch do
11
+ create_table :all_types do |t|
12
+ t.column :col_string, :string
13
+ t.column :col_int64, :bigint
14
+ t.column :col_float64, :float
15
+ t.column :col_numeric, :numeric
16
+ t.column :col_bool, :boolean
17
+ t.column :col_bytes, :binary
18
+ t.column :col_date, :date
19
+ t.column :col_timestamp, :datetime
20
+
21
+ t.column :col_array_string, :string, array: true
22
+ t.column :col_array_int64, :bigint, array: true
23
+ t.column :col_array_float64, :float, array: true
24
+ t.column :col_array_numeric, :numeric, array: true
25
+ t.column :col_array_bool, :boolean, array: true
26
+ t.column :col_array_bytes, :binary, array: true
27
+ t.column :col_array_date, :date, array: true
28
+ t.column :col_array_timestamp, :datetime, array: true
29
+ end
30
+
31
+ create_table :firms do |t|
32
+ t.string :name
33
+ t.integer :rating
34
+ t.string :description
35
+ t.references :account
36
+ end
37
+
38
+ create_table :customers do |t|
39
+ t.string :name
40
+ end
41
+
42
+ create_table :accounts do |t|
43
+ t.references :customer, index: false
44
+ t.references :firm, index: false
45
+ t.string :name
46
+ t.integer :credit_limit
47
+ t.integer :transactions_count
48
+ end
49
+
50
+ create_table :transactions do |t|
51
+ t.float :amount
52
+ t.references :account, index: false
53
+ end
54
+
55
+ create_table :departments do |t|
56
+ t.string :name
57
+ t.references :resource, polymorphic: true
58
+ end
59
+
60
+ create_table :member_types do |t|
61
+ t.string :name
62
+ end
63
+
64
+ create_table :members do |t|
65
+ t.string :name
66
+ t.references :member_type, index: false
67
+ t.references :admittable, polymorphic: true, index: false
68
+ end
69
+
70
+ create_table :memberships do |t|
71
+ t.datetime :joined_on
72
+ t.references :club, index: false
73
+ t.references :member, index: false
74
+ t.boolean :favourite
75
+ end
76
+
77
+ create_table :clubs do |t|
78
+ t.string :name
79
+ end
80
+
81
+ create_table :authors do |t|
82
+ t.string :name, null: false
83
+ t.date :registered_date
84
+ t.references :organization, index: false
85
+ end
86
+
87
+ create_table :posts do |t|
88
+ t.string :title
89
+ t.string :content
90
+ t.references :author
91
+ t.integer :comments_count
92
+ t.date :post_date
93
+ t.time :published_time
94
+ end
95
+
96
+ create_table :comments do |t|
97
+ t.string :comment
98
+ t.references :post, index: false, foreign_key: true
99
+ end
100
+
101
+ create_table :addresses do |t|
102
+ t.string :line1
103
+ t.string :postal_code
104
+ t.string :city
105
+ t.references :author, index: false
106
+ end
107
+
108
+ create_table :organizations do |t|
109
+ t.string :name
110
+ t.datetime :last_updated, allow_commit_timestamp: true
111
+ end
112
+
113
+ create_table :singers, id: false do |t|
114
+ t.primary_key :singerid
115
+ t.column :first_name, :string, limit: 200
116
+ t.string :last_name
117
+ t.integer :tracks_count
118
+ t.integer :lock_version
119
+ t.string :full_name, as: "COALESCE(first_name || ' ', '') || last_name", stored: true
120
+ end
121
+
122
+ create_table :albums, id: false do |t|
123
+ t.interleave_in :singers
124
+ t.primary_key :albumid
125
+ # `singerid` is part of the primary key in the table definition, but it is not visible to ActiveRecord as part of
126
+ # the primary key, to prevent ActiveRecord from considering this to be an entity with a composite primary key.
127
+ t.parent_key :singerid
128
+ t.string :title
129
+ t.integer :lock_version
130
+ end
131
+
132
+ create_table :tracks, id: false do |t|
133
+ # `:cascade` causes all tracks that belong to an album to automatically be deleted when an album is deleted.
134
+ t.interleave_in :albums, :cascade
135
+ t.primary_key :trackid
136
+ t.parent_key :singerid
137
+ t.parent_key :albumid
138
+ t.string :title
139
+ t.numeric :duration
140
+ t.integer :lock_version
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,260 @@
1
+ # Copyright 2020 Google LLC
2
+ #
3
+ # Use of this source code is governed by an MIT-style
4
+ # license that can be found in the LICENSE file or at
5
+ # https://opensource.org/licenses/MIT.
6
+
7
+ gem "minitest"
8
+ require "minitest/autorun"
9
+ require "minitest/focus"
10
+ require "minitest/rg"
11
+ require "active_support"
12
+ require "google/cloud/spanner"
13
+ require "active_record"
14
+ require "active_support/testing/stream"
15
+ require "activerecord-spanner-adapter"
16
+ require "active_record/connection_adapters/spanner_adapter"
17
+ require "securerandom"
18
+
19
+ # rubocop:disable Style/GlobalVars
20
+
21
+ $spanner_test_database = "ar-test-#{SecureRandom.hex 4}"
22
+
23
+ def connector_config
24
+ {
25
+ "adapter" => "spanner",
26
+ "emulator_host" => ENV["SPANNER_EMULATOR_HOST"],
27
+ "project" => ENV["SPANNER_TEST_PROJECT"],
28
+ "instance" => ENV["SPANNER_TEST_INSTANCE"],
29
+ "credentials" => ENV["SPANNER_TEST_KEYFILE"],
30
+ "database" => $spanner_test_database
31
+ }
32
+ end
33
+
34
+ def spanner
35
+ $spanner ||= Google::Cloud::Spanner.new(
36
+ project_id: ENV["SPANNER_TEST_PROJECT"],
37
+ credentials: ENV["SPANNER_TEST_KEYFILE"]
38
+ )
39
+ end
40
+
41
+ def spanner_instance
42
+ unless spanner.instance ENV["SPANNER_TEST_INSTANCE"]
43
+ config = ENV["SPANNER_EMULATOR_HOST"] ? "emulator-config" : "regional-us-central1"
44
+ puts "Creating test instance #{ENV["SPANNER_TEST_INSTANCE"]} with config #{config}"
45
+ job = spanner.create_instance ENV["SPANNER_TEST_INSTANCE"],
46
+ name: "ActiveRecord Test Instance",
47
+ config: config,
48
+ nodes: 1
49
+ job.wait_until_done!
50
+ $owned_test_instance = true
51
+ end
52
+ $spanner_instance ||= spanner.instance ENV["SPANNER_TEST_INSTANCE"]
53
+ end
54
+
55
+ def create_test_database
56
+ job = spanner_instance.create_database $spanner_test_database
57
+ job.wait_until_done!
58
+ if job.error?
59
+ raise "Error in creating database. Error code#{job.error.message}"
60
+ end
61
+
62
+ puts "'#{$spanner_test_database}' test db created."
63
+
64
+ puts "Loading test schema..."
65
+ ActiveRecord::Base.establish_connection connector_config
66
+ require_relative "schema/schema"
67
+ end
68
+
69
+ def drop_test_database
70
+ ActiveRecord::Base.connection_pool.disconnect!
71
+ spanner_instance&.delete if $owned_test_instance
72
+ spanner_instance.database($spanner_test_database)&.drop unless $owned_test_instance
73
+
74
+ puts "Test instance #{spanner_instance} deleted" if $owned_test_instance
75
+ puts "#{$spanner_test_database} database deleted" unless $owned_test_instance
76
+ end
77
+
78
+ def current_adapter? *names
79
+ names.include? :SpannerAdapter
80
+ end
81
+
82
+ def load_test_schema
83
+ ActiveRecord::Base.establish_connection connector_config
84
+
85
+ require_relative "schema/schema"
86
+ end
87
+
88
+ module SpannerAdapter
89
+ class TestCase < ActiveSupport::TestCase
90
+ def assert_column(model, column_name, msg = nil)
91
+ assert has_column?(model, column_name), msg
92
+ end
93
+
94
+ def assert_no_column(model, column_name, msg = nil)
95
+ assert_not has_column?(model, column_name), msg
96
+ end
97
+
98
+ def has_column?(model, column_name)
99
+ model.reset_column_information
100
+ model.column_names.include?(column_name.to_s)
101
+ end
102
+
103
+ def capture_sql
104
+ ActiveRecord::Base.connection.materialize_transactions
105
+ SQLCounter.clear_log
106
+ yield
107
+ SQLCounter.log.dup
108
+ end
109
+
110
+ def assert_queries(num = 1, options = {})
111
+ ignore_none = options.fetch(:ignore_none) { num == :any }
112
+ ActiveRecord::Base.connection.materialize_transactions
113
+ SQLCounter.clear_log
114
+ x = yield
115
+ the_log = ignore_none ? SQLCounter.log_all : SQLCounter.log
116
+ if num == :any
117
+ assert_operator the_log.size, :>=, 1, "1 or more queries expected, but none were executed."
118
+ else
119
+ mesg = "#{the_log.size} instead of #{num} queries were executed.#{the_log.size == 0 ? '' : "\nQueries:\n#{the_log.join("\n")}"}"
120
+ assert_equal num, the_log.size, mesg
121
+ end
122
+ x
123
+ end
124
+
125
+ def assert_no_queries(options = {}, &block)
126
+ options.reverse_merge! ignore_none: true
127
+ assert_queries(0, options, &block)
128
+ end
129
+ end
130
+
131
+ module Migration
132
+ module TestHelper
133
+ attr_accessor :connection
134
+
135
+ CONNECTION_METHODS = %w[
136
+ add_column remove_column rename_column add_index change_column
137
+ rename_table column_exists? index_exists?
138
+ add_reference add_belongs_to remove_reference remove_references
139
+ remove_belongs_to change_column_default
140
+ ].freeze
141
+
142
+ class TestModel < ActiveRecord::Base
143
+ self.table_name = :test_models
144
+ end
145
+
146
+ def setup
147
+ ActiveRecord::Base.establish_connection connector_config
148
+ @connection = ActiveRecord::Base.connection
149
+
150
+ unless @skip_test_table_create
151
+ connection.create_table :test_models do |t|
152
+ t.timestamps null: true
153
+ end
154
+
155
+ TestModel.reset_column_information
156
+ end
157
+ super
158
+ end
159
+
160
+ def skip_test_table_create!
161
+ @skip_test_table_create = true
162
+ end
163
+
164
+ def teardown
165
+ TestModel.reset_table_name
166
+
167
+ unless @skip_test_table_create
168
+ connection.drop_table :test_models, if_exists: true
169
+ end
170
+
171
+ super
172
+ end
173
+
174
+ def generate_id
175
+ connection.next_sequence_value nil
176
+ end
177
+
178
+ delegate *CONNECTION_METHODS, to: :connection
179
+ end
180
+ end
181
+
182
+ module Types
183
+ module TestHelper
184
+ attr_accessor :connection
185
+
186
+ class TestTypeModel < ActiveRecord::Base
187
+ self.table_name = :test_types
188
+ end
189
+
190
+ def setup
191
+ super
192
+
193
+ ActiveRecord::Base.establish_connection connector_config
194
+ @connection = ActiveRecord::Base.connection
195
+
196
+ return if connection.table_exists? :test_types
197
+
198
+ connection.create_table :test_types do |t|
199
+ t.string :name, limit: 255
200
+ t.string :description
201
+ t.text :bio
202
+ t.integer :length
203
+ t.float :weight
204
+ t.numeric :price
205
+ t.boolean :active
206
+ t.binary :file
207
+ t.binary :data, limit: 255
208
+ t.date :start_date
209
+ t.datetime :start_datetime
210
+ t.time :start_time
211
+ end
212
+ end
213
+
214
+ def teardown
215
+ super
216
+ TestTypeModel.delete_all
217
+ end
218
+ end
219
+ end
220
+
221
+ module Associations
222
+ module TestHelper
223
+ def setup
224
+ ActiveRecord::Base.establish_connection connector_config
225
+ @connection = ActiveRecord::Base.connection
226
+ end
227
+ end
228
+ end
229
+
230
+ class SQLCounter
231
+ class << self
232
+ attr_accessor :ignored_sql, :log, :log_all
233
+
234
+ def clear_log
235
+ self.log = []
236
+ self.log_all = []
237
+ end
238
+ end
239
+
240
+ clear_log
241
+
242
+ def call(name, start, finish, message_id, values)
243
+ return if values[:cached]
244
+
245
+ sql = values[:sql]
246
+ self.class.log_all << sql
247
+ self.class.log << sql unless ["SCHEMA", "TRANSACTION"].include? values[:name]
248
+ end
249
+ end
250
+
251
+ ActiveSupport::Notifications.subscribe("sql.active_record", SQLCounter.new)
252
+ end
253
+
254
+ Minitest.after_run do
255
+ drop_test_database
256
+ end
257
+
258
+ create_test_database
259
+
260
+ # rubocop:enable Style/GlobalVars