activerecord-spanner-adapter 0.3.0 → 1.0.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 (282) 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 +49 -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 +55 -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 +66 -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 +171 -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 +130 -0
  56. data/acceptance/cases/transactions/read_write_transactions_test.rb +248 -0
  57. data/acceptance/cases/type/all_types_test.rb +172 -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/json_test.rb +34 -0
  65. data/acceptance/cases/type/numeric_test.rb +27 -0
  66. data/acceptance/cases/type/string_test.rb +79 -0
  67. data/acceptance/cases/type/text_test.rb +30 -0
  68. data/acceptance/cases/type/time_test.rb +87 -0
  69. data/acceptance/models/account.rb +13 -0
  70. data/acceptance/models/address.rb +9 -0
  71. data/acceptance/models/album.rb +12 -0
  72. data/acceptance/models/all_types.rb +8 -0
  73. data/acceptance/models/author.rb +11 -0
  74. data/acceptance/models/club.rb +12 -0
  75. data/acceptance/models/comment.rb +9 -0
  76. data/acceptance/models/customer.rb +9 -0
  77. data/acceptance/models/department.rb +9 -0
  78. data/acceptance/models/firm.rb +10 -0
  79. data/acceptance/models/member.rb +13 -0
  80. data/acceptance/models/member_type.rb +9 -0
  81. data/acceptance/models/membership.rb +10 -0
  82. data/acceptance/models/organization.rb +9 -0
  83. data/acceptance/models/post.rb +10 -0
  84. data/acceptance/models/singer.rb +10 -0
  85. data/acceptance/models/track.rb +20 -0
  86. data/acceptance/models/transaction.rb +9 -0
  87. data/acceptance/schema/schema.rb +147 -0
  88. data/acceptance/test_helper.rb +261 -0
  89. data/activerecord-spanner-adapter.gemspec +32 -17
  90. data/assets/solidus-db.png +0 -0
  91. data/benchmarks/README.md +17 -0
  92. data/benchmarks/Rakefile +14 -0
  93. data/benchmarks/application.rb +308 -0
  94. data/benchmarks/config/database.yml +8 -0
  95. data/benchmarks/config/environment.rb +12 -0
  96. data/benchmarks/db/migrate/01_create_tables.rb +25 -0
  97. data/benchmarks/db/schema.rb +29 -0
  98. data/benchmarks/models/album.rb +9 -0
  99. data/benchmarks/models/singer.rb +9 -0
  100. data/bin/console +6 -7
  101. data/examples/rails/README.md +262 -0
  102. data/examples/snippets/README.md +29 -0
  103. data/examples/snippets/Rakefile +57 -0
  104. data/examples/snippets/array-data-type/README.md +45 -0
  105. data/examples/snippets/array-data-type/Rakefile +13 -0
  106. data/examples/snippets/array-data-type/application.rb +45 -0
  107. data/examples/snippets/array-data-type/config/database.yml +8 -0
  108. data/examples/snippets/array-data-type/db/migrate/01_create_tables.rb +24 -0
  109. data/examples/snippets/array-data-type/db/schema.rb +26 -0
  110. data/examples/snippets/array-data-type/db/seeds.rb +5 -0
  111. data/examples/snippets/array-data-type/models/entity_with_array_types.rb +18 -0
  112. data/examples/snippets/bin/create_emulator_instance.rb +18 -0
  113. data/examples/snippets/bulk-insert/README.md +21 -0
  114. data/examples/snippets/bulk-insert/Rakefile +13 -0
  115. data/examples/snippets/bulk-insert/application.rb +64 -0
  116. data/examples/snippets/bulk-insert/config/database.yml +8 -0
  117. data/examples/snippets/bulk-insert/db/migrate/01_create_tables.rb +21 -0
  118. data/examples/snippets/bulk-insert/db/schema.rb +26 -0
  119. data/examples/snippets/bulk-insert/db/seeds.rb +5 -0
  120. data/examples/snippets/bulk-insert/models/album.rb +9 -0
  121. data/examples/snippets/bulk-insert/models/singer.rb +9 -0
  122. data/examples/snippets/commit-timestamp/README.md +18 -0
  123. data/examples/snippets/commit-timestamp/Rakefile +13 -0
  124. data/examples/snippets/commit-timestamp/application.rb +53 -0
  125. data/examples/snippets/commit-timestamp/config/database.yml +8 -0
  126. data/examples/snippets/commit-timestamp/db/migrate/01_create_tables.rb +26 -0
  127. data/examples/snippets/commit-timestamp/db/schema.rb +29 -0
  128. data/examples/snippets/commit-timestamp/db/seeds.rb +5 -0
  129. data/examples/snippets/commit-timestamp/models/album.rb +9 -0
  130. data/examples/snippets/commit-timestamp/models/singer.rb +9 -0
  131. data/examples/snippets/config/environment.rb +21 -0
  132. data/examples/snippets/create-records/README.md +12 -0
  133. data/examples/snippets/create-records/Rakefile +13 -0
  134. data/examples/snippets/create-records/application.rb +42 -0
  135. data/examples/snippets/create-records/config/database.yml +8 -0
  136. data/examples/snippets/create-records/db/migrate/01_create_tables.rb +21 -0
  137. data/examples/snippets/create-records/db/schema.rb +26 -0
  138. data/examples/snippets/create-records/db/seeds.rb +5 -0
  139. data/examples/snippets/create-records/models/album.rb +9 -0
  140. data/examples/snippets/create-records/models/singer.rb +9 -0
  141. data/examples/snippets/date-data-type/README.md +19 -0
  142. data/examples/snippets/date-data-type/Rakefile +13 -0
  143. data/examples/snippets/date-data-type/application.rb +35 -0
  144. data/examples/snippets/date-data-type/config/database.yml +8 -0
  145. data/examples/snippets/date-data-type/db/migrate/01_create_tables.rb +20 -0
  146. data/examples/snippets/date-data-type/db/schema.rb +21 -0
  147. data/examples/snippets/date-data-type/db/seeds.rb +16 -0
  148. data/examples/snippets/date-data-type/models/singer.rb +8 -0
  149. data/examples/snippets/generated-column/README.md +41 -0
  150. data/examples/snippets/generated-column/Rakefile +13 -0
  151. data/examples/snippets/generated-column/application.rb +37 -0
  152. data/examples/snippets/generated-column/config/database.yml +8 -0
  153. data/examples/snippets/generated-column/db/migrate/01_create_tables.rb +23 -0
  154. data/examples/snippets/generated-column/db/schema.rb +21 -0
  155. data/examples/snippets/generated-column/db/seeds.rb +18 -0
  156. data/examples/snippets/generated-column/models/singer.rb +8 -0
  157. data/examples/snippets/hints/README.md +19 -0
  158. data/examples/snippets/hints/Rakefile +13 -0
  159. data/examples/snippets/hints/application.rb +47 -0
  160. data/examples/snippets/hints/config/database.yml +8 -0
  161. data/examples/snippets/hints/db/migrate/01_create_tables.rb +23 -0
  162. data/examples/snippets/hints/db/schema.rb +28 -0
  163. data/examples/snippets/hints/db/seeds.rb +29 -0
  164. data/examples/snippets/hints/models/album.rb +9 -0
  165. data/examples/snippets/hints/models/singer.rb +9 -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/partitioned-dml/README.md +16 -0
  195. data/examples/snippets/partitioned-dml/Rakefile +13 -0
  196. data/examples/snippets/partitioned-dml/application.rb +48 -0
  197. data/examples/snippets/partitioned-dml/config/database.yml +8 -0
  198. data/examples/snippets/partitioned-dml/db/migrate/01_create_tables.rb +21 -0
  199. data/examples/snippets/partitioned-dml/db/schema.rb +26 -0
  200. data/examples/snippets/partitioned-dml/db/seeds.rb +29 -0
  201. data/examples/snippets/partitioned-dml/models/album.rb +9 -0
  202. data/examples/snippets/partitioned-dml/models/singer.rb +9 -0
  203. data/examples/snippets/quickstart/README.md +26 -0
  204. data/examples/snippets/quickstart/Rakefile +13 -0
  205. data/examples/snippets/quickstart/application.rb +51 -0
  206. data/examples/snippets/quickstart/config/database.yml +8 -0
  207. data/examples/snippets/quickstart/db/migrate/01_create_tables.rb +21 -0
  208. data/examples/snippets/quickstart/db/schema.rb +26 -0
  209. data/examples/snippets/quickstart/db/seeds.rb +24 -0
  210. data/examples/snippets/quickstart/models/album.rb +9 -0
  211. data/examples/snippets/quickstart/models/singer.rb +9 -0
  212. data/examples/snippets/read-only-transactions/README.md +13 -0
  213. data/examples/snippets/read-only-transactions/Rakefile +13 -0
  214. data/examples/snippets/read-only-transactions/application.rb +77 -0
  215. data/examples/snippets/read-only-transactions/config/database.yml +8 -0
  216. data/examples/snippets/read-only-transactions/db/migrate/01_create_tables.rb +21 -0
  217. data/examples/snippets/read-only-transactions/db/schema.rb +26 -0
  218. data/examples/snippets/read-only-transactions/db/seeds.rb +24 -0
  219. data/examples/snippets/read-only-transactions/models/album.rb +9 -0
  220. data/examples/snippets/read-only-transactions/models/singer.rb +9 -0
  221. data/examples/snippets/read-write-transactions/README.md +12 -0
  222. data/examples/snippets/read-write-transactions/Rakefile +13 -0
  223. data/examples/snippets/read-write-transactions/application.rb +39 -0
  224. data/examples/snippets/read-write-transactions/config/database.yml +8 -0
  225. data/examples/snippets/read-write-transactions/db/migrate/01_create_tables.rb +22 -0
  226. data/examples/snippets/read-write-transactions/db/schema.rb +27 -0
  227. data/examples/snippets/read-write-transactions/db/seeds.rb +25 -0
  228. data/examples/snippets/read-write-transactions/models/album.rb +9 -0
  229. data/examples/snippets/read-write-transactions/models/singer.rb +9 -0
  230. data/examples/snippets/stale-reads/README.md +27 -0
  231. data/examples/snippets/stale-reads/Rakefile +13 -0
  232. data/examples/snippets/stale-reads/application.rb +63 -0
  233. data/examples/snippets/stale-reads/config/database.yml +8 -0
  234. data/examples/snippets/stale-reads/db/migrate/01_create_tables.rb +21 -0
  235. data/examples/snippets/stale-reads/db/schema.rb +26 -0
  236. data/examples/snippets/stale-reads/db/seeds.rb +24 -0
  237. data/examples/snippets/stale-reads/models/album.rb +9 -0
  238. data/examples/snippets/stale-reads/models/singer.rb +9 -0
  239. data/examples/snippets/timestamp-data-type/README.md +17 -0
  240. data/examples/snippets/timestamp-data-type/Rakefile +13 -0
  241. data/examples/snippets/timestamp-data-type/application.rb +42 -0
  242. data/examples/snippets/timestamp-data-type/config/database.yml +8 -0
  243. data/examples/snippets/timestamp-data-type/db/migrate/01_create_tables.rb +21 -0
  244. data/examples/snippets/timestamp-data-type/db/schema.rb +21 -0
  245. data/examples/snippets/timestamp-data-type/db/seeds.rb +6 -0
  246. data/examples/snippets/timestamp-data-type/models/meeting.rb +19 -0
  247. data/examples/solidus/README.md +172 -0
  248. data/lib/active_record/connection_adapters/spanner/database_statements.rb +244 -266
  249. data/lib/active_record/connection_adapters/spanner/quoting.rb +42 -50
  250. data/lib/active_record/connection_adapters/spanner/schema_cache.rb +43 -0
  251. data/lib/active_record/connection_adapters/spanner/schema_creation.rb +125 -9
  252. data/lib/active_record/connection_adapters/spanner/schema_definitions.rb +122 -0
  253. data/lib/active_record/connection_adapters/spanner/schema_dumper.rb +19 -0
  254. data/lib/active_record/connection_adapters/spanner/schema_statements.rb +553 -139
  255. data/lib/active_record/connection_adapters/spanner/type_metadata.rb +37 -0
  256. data/lib/active_record/connection_adapters/spanner_adapter.rb +185 -78
  257. data/lib/active_record/tasks/spanner_database_tasks.rb +74 -0
  258. data/lib/active_record/type/spanner/array.rb +32 -0
  259. data/lib/active_record/type/spanner/bytes.rb +26 -0
  260. data/lib/active_record/type/spanner/spanner_active_record_converter.rb +33 -0
  261. data/lib/active_record/type/spanner/time.rb +37 -0
  262. data/lib/activerecord-spanner-adapter.rb +23 -0
  263. data/lib/activerecord_spanner_adapter/base.rb +238 -0
  264. data/lib/activerecord_spanner_adapter/connection.rb +350 -0
  265. data/lib/activerecord_spanner_adapter/errors.rb +13 -0
  266. data/lib/activerecord_spanner_adapter/foreign_key.rb +29 -0
  267. data/lib/activerecord_spanner_adapter/index/column.rb +38 -0
  268. data/lib/activerecord_spanner_adapter/index.rb +80 -0
  269. data/lib/activerecord_spanner_adapter/information_schema.rb +262 -0
  270. data/lib/activerecord_spanner_adapter/primary_key.rb +31 -0
  271. data/lib/activerecord_spanner_adapter/table/column.rb +59 -0
  272. data/lib/activerecord_spanner_adapter/table.rb +61 -0
  273. data/lib/activerecord_spanner_adapter/transaction.rb +154 -0
  274. data/lib/activerecord_spanner_adapter/version.rb +9 -0
  275. data/lib/arel/visitors/spanner.rb +111 -0
  276. data/lib/spanner_client_ext.rb +107 -0
  277. data/renovate.json +5 -0
  278. metadata +405 -34
  279. data/.travis.yml +0 -5
  280. data/lib/active_record/connection_adapters/spanner/client.rb +0 -190
  281. data/lib/active_record/connection_adapters/spanner.rb +0 -10
  282. data/lib/activerecord-spanner-adapter/version.rb +0 -3
@@ -0,0 +1,433 @@
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
+ require "test_helper"
10
+
11
+ module ActiveRecord
12
+ class Migration
13
+ class ChangeSchemaTest < SpannerAdapter::TestCase
14
+ include SpannerAdapter::Migration::TestHelper
15
+
16
+ attr_reader :connection, :table_name
17
+
18
+ def setup
19
+ skip_test_table_create!
20
+ super
21
+ @table_name = :testings
22
+ end
23
+
24
+ def teardown
25
+ connection.ddl_batch do
26
+ connection.drop_table :testings rescue nil
27
+ end
28
+ ActiveRecord::Base.primary_key_prefix_type = nil
29
+ ActiveRecord::Base.clear_cache!
30
+ end
31
+
32
+ def test_add_column
33
+ testing_table_with_only_foo_attribute do
34
+ assert_equal connection.columns(:testings).size, 2
35
+ connection.add_column :testings, :name, :string
36
+ assert_equal connection.columns(:testings).size, 3
37
+ end
38
+ end
39
+
40
+ def test_create_table_adds_id
41
+ connection.ddl_batch do
42
+ connection.create_table :testings do |t|
43
+ t.column :foo, :string
44
+ end
45
+ end
46
+
47
+ assert_equal %w[id foo].sort, connection.columns(:testings).map(&:name).sort
48
+ assert_equal "id", connection.primary_key(:testings)
49
+
50
+ column = connection.columns(:testings).find { |c| c.name == "id" }
51
+
52
+ assert_equal "id", column.name
53
+ assert_equal :integer, column.type
54
+ end
55
+
56
+ def test_create_table_with_custom_primary_key
57
+ connection.ddl_batch do
58
+ connection.create_table :testings, force: true, primary_key: :email do |t|
59
+ t.string :name
60
+ end
61
+ end
62
+
63
+ assert_equal "email", connection.primary_key(:testings)
64
+ end
65
+
66
+ def test_create_table_with_custom_primary_key_using_options
67
+ connection.ddl_batch do
68
+ connection.create_table :testings, force: true, id: false do |t|
69
+ t.string :phone, primary_key: true
70
+ t.string :name
71
+ end
72
+ end
73
+
74
+ assert_equal "phone", connection.primary_key(:testings)
75
+ end
76
+
77
+ def test_create_table_with_custom_primary_key_using_type
78
+ connection.ddl_batch do
79
+ connection.create_table :testings, force: true, id: false do |t|
80
+ t.primary_key :username, limit: 255
81
+ t.string :name
82
+ end
83
+ end
84
+
85
+ assert_equal "username", connection.primary_key(:testings)
86
+
87
+ column = connection.columns(:testings).find { |c| c.name == "username" }
88
+
89
+ assert_equal "username", column.name
90
+ assert_equal :integer, column.type
91
+ end
92
+
93
+ def test_create_table_with_custom_primary_key_type
94
+ connection.ddl_batch do
95
+ connection.create_table :testings, force: true, id: false do |t|
96
+ t.primary_key :user_id, :integer
97
+ t.string :name
98
+ end
99
+ end
100
+
101
+ assert_equal "user_id", connection.primary_key(:testings)
102
+
103
+ column = connection.columns(:testings).find { |c| c.name == "user_id" }
104
+
105
+ assert_equal "user_id", column.name
106
+ assert_equal :integer, column.type
107
+ end
108
+
109
+ def test_create_table_with_not_null_column
110
+ connection.ddl_batch do
111
+ connection.create_table :testings do |t|
112
+ t.column :foo, :string, null: false
113
+ end
114
+ end
115
+
116
+ assert_raises ActiveRecord::StatementInvalid do
117
+ connection.transaction {
118
+ connection.execute "insert into testings (id, foo) values (#{generate_id}, NULL)"
119
+ }
120
+ end
121
+ end
122
+
123
+ def test_create_table_with_limits
124
+ connection.ddl_batch do
125
+ connection.create_table :testings do |t|
126
+ t.column :foo, :string, limit: 255
127
+ t.column :default_int, :integer
128
+ end
129
+ end
130
+
131
+ columns = connection.columns :testings
132
+ foo = columns.detect { |c| c.name == "foo" }
133
+ assert_equal "STRING(255)", foo.sql_type
134
+ assert_equal 255, foo.limit
135
+
136
+ default_int = columns.detect { |c| c.name == "default_int" }
137
+ assert_equal "INT64", default_int.sql_type
138
+ end
139
+
140
+ def test_create_table_with_primary_key_prefix_as_table_name_with_underscore
141
+ ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore
142
+
143
+ connection.ddl_batch do
144
+ connection.create_table :testings do |t|
145
+ t.column :foo, :string
146
+ end
147
+ end
148
+
149
+ assert_equal "testing_id", connection.primary_key(:testings)
150
+
151
+ columns = connection.columns(:testings)
152
+ assert_equal %w[testing_id foo].sort, columns.map(&:name).sort
153
+ end
154
+
155
+ def test_create_table_with_primary_key_prefix_as_table_name
156
+ ActiveRecord::Base.primary_key_prefix_type = :table_name
157
+
158
+ connection.ddl_batch do
159
+ connection.create_table :testings do |t|
160
+ t.column :foo, :string
161
+ end
162
+ end
163
+
164
+ assert_equal "testingid", connection.primary_key(:testings)
165
+
166
+ columns = connection.columns(:testings)
167
+ assert_equal %w[testingid foo].sort, columns.map(&:name).sort
168
+ end
169
+
170
+ def test_create_table_raises_when_redefining_primary_key_column
171
+ error = assert_raise ArgumentError do
172
+ connection.ddl_batch do
173
+ connection.create_table :testings do |t|
174
+ t.column :id, :string
175
+ end
176
+ end
177
+ end
178
+
179
+ assert_equal "you can't redefine the primary key column 'id'. To define a custom primary key, pass { id: false } to create_table.", error.message
180
+ end
181
+
182
+ def test_create_table_raises_when_redefining_custom_primary_key_column
183
+ error = assert_raise ArgumentError do
184
+ connection.ddl_batch do
185
+ connection.create_table :testings, primary_key: :testing_id do |t|
186
+ t.column :testing_id, :string
187
+ end
188
+ end
189
+ end
190
+
191
+ assert_equal "you can't redefine the primary key column 'testing_id'. To define a custom primary key, pass { id: false } to create_table.", error.message
192
+ end
193
+
194
+ def test_create_table_raises_when_defining_existing_column
195
+ error = assert_raise ArgumentError do
196
+ connection.ddl_batch do
197
+ connection.create_table :testings do |t|
198
+ t.column :testing_column, :string
199
+ t.column :testing_column, :integer
200
+ end
201
+ end
202
+ end
203
+
204
+ assert_equal "you can't define an already defined column 'testing_column'.", error.message
205
+ end
206
+
207
+ def test_create_table_with_timestamps_should_create_datetime_columns
208
+ connection.ddl_batch do
209
+ connection.create_table table_name do |t|
210
+ t.timestamps
211
+ end
212
+ end
213
+ created_columns = connection.columns table_name
214
+
215
+ created_at_column = created_columns.detect { |c| c.name == "created_at" }
216
+ updated_at_column = created_columns.detect { |c| c.name == "updated_at" }
217
+
218
+ assert_not created_at_column.null
219
+ assert_not updated_at_column.null
220
+ end
221
+
222
+ def test_create_table_with_timestamps_should_create_datetime_columns_with_options
223
+ connection.ddl_batch do
224
+ connection.create_table table_name do |t|
225
+ t.timestamps null: true
226
+ end
227
+ end
228
+ created_columns = connection.columns table_name
229
+
230
+ created_at_column = created_columns.detect { |c| c.name == "created_at" }
231
+ updated_at_column = created_columns.detect { |c| c.name == "updated_at" }
232
+
233
+ assert created_at_column.null
234
+ assert updated_at_column.null
235
+ end
236
+
237
+ def test_create_table_without_a_block
238
+ connection.create_table table_name
239
+ end
240
+
241
+ def test_add_column_not_null_without_default
242
+ skip "Unimplemented error: Cannot add NOT NULL column to existing table"
243
+ connection.ddl_batch do
244
+ connection.create_table :testings do |t|
245
+ t.column :foo, :string
246
+ end
247
+ connection.add_column :testings, :bar, :string, null: false
248
+ end
249
+
250
+ assert_raise ActiveRecord::NotNullViolation do
251
+ connection.transaction {
252
+ connection.execute "insert into testings (id, foo, bar) values (#{generate_id}, 'hello', NULL)"
253
+ }
254
+ end
255
+ end
256
+
257
+ def test_add_column_with_timestamp_type
258
+ connection.ddl_batch do
259
+ connection.create_table :testings do |t|
260
+ t.column :foo, :timestamp
261
+ end
262
+ end
263
+
264
+ column = connection.columns(:testings).find { |c| c.name == "foo" }
265
+
266
+ assert_equal :time, column.type
267
+ assert_equal "TIMESTAMP", column.sql_type
268
+ end
269
+
270
+ def test_change_column_quotes_column_names
271
+ connection.ddl_batch do
272
+ connection.create_table :testings do |t|
273
+ t.column :select, :string
274
+ end
275
+ end
276
+ # These two changes cannot be in one batch, as the change_column operation will check whether the column
277
+ # actually exists before executing any DDL statements.
278
+ connection.ddl_batch do
279
+ connection.change_column :testings, :select, :string, limit: 10
280
+ end
281
+
282
+ connection.transaction {
283
+ connection.execute "insert into testings (#{connection.quote_column_name 'id'},"\
284
+ "#{connection.quote_column_name 'select'}) values (#{generate_id}, '7 chars')"
285
+ }
286
+ end
287
+
288
+ def test_keeping_notnull_constraints_on_change
289
+ person_klass = Class.new ActiveRecord::Base
290
+ connection.ddl_batch do
291
+ connection.create_table :testings do |t|
292
+ t.column :title, :string
293
+ end
294
+ person_klass.table_name = "testings"
295
+ person_klass.connection.add_column "testings", "wealth", :integer, null: false
296
+ end
297
+
298
+ person_klass.reset_column_information
299
+ assert_equal false, person_klass.columns_hash["wealth"].null
300
+
301
+ assert_nothing_raised {
302
+ person_klass.connection.transaction {
303
+ person_klass.connection.execute("insert into testings (id, title, wealth) values (#{generate_id}, 'tester', 100000)")
304
+ }
305
+ }
306
+
307
+ # change column, make it nullable
308
+ connection.ddl_batch do
309
+ person_klass.connection.change_column "testings", "wealth", :integer, null: true
310
+ end
311
+ person_klass.reset_column_information
312
+ assert_nil person_klass.columns_hash["wealth"].default
313
+ assert_equal true, person_klass.columns_hash["wealth"].null
314
+ end
315
+
316
+ def test_change_column_null
317
+ testing_table_with_only_foo_attribute do
318
+ notnull_migration = Class.new(ActiveRecord::Migration::Current) do
319
+ def change
320
+ change_column_null :testings, :foo, false
321
+ end
322
+ end
323
+ notnull_migration.new.suppress_messages do
324
+ notnull_migration.migrate(:up)
325
+ assert_equal false, connection.columns(:testings).find { |c| c.name == "foo" }.null
326
+ notnull_migration.migrate(:down)
327
+ assert connection.columns(:testings).find { |c| c.name == "foo" }.null
328
+ end
329
+ end
330
+ end
331
+
332
+ def test_column_exists
333
+ connection.ddl_batch do
334
+ connection.create_table :testings do |t|
335
+ t.column :foo, :string
336
+ end
337
+ end
338
+
339
+ assert connection.column_exists?(:testings, :foo)
340
+ assert_not connection.column_exists?(:testings, :bar)
341
+ end
342
+
343
+ def test_column_exists_with_type
344
+ connection.ddl_batch do
345
+ connection.create_table :testings do |t|
346
+ t.column :foo, :string
347
+ t.column :bar, :float
348
+ end
349
+ end
350
+
351
+ assert connection.column_exists?(:testings, :foo, :string)
352
+ assert_not connection.column_exists?(:testings, :foo, :integer)
353
+
354
+ assert connection.column_exists?(:testings, :bar, :float)
355
+ assert_not connection.column_exists?(:testings, :bar, :integer)
356
+ end
357
+
358
+ def test_column_exists_with_definition
359
+ connection.ddl_batch do
360
+ connection.create_table :testings do |t|
361
+ t.column :foo, :string, limit: 100
362
+ t.column :bar, :float
363
+ t.column :taggable_id, :integer, null: false
364
+ t.column :taggable_type, :string
365
+ end
366
+ end
367
+
368
+ assert connection.column_exists?(:testings, :foo, :string, limit: 100)
369
+ assert_not connection.column_exists?(:testings, :foo, :string, limit: nil)
370
+ assert connection.column_exists?(:testings, :bar, :float)
371
+ assert connection.column_exists?(:testings, :taggable_id, :integer, null: false)
372
+ assert_not connection.column_exists?(:testings, :taggable_id, :integer, null: true)
373
+ assert connection.column_exists?(:testings, :taggable_type, :string, default: nil)
374
+ end
375
+
376
+ def test_column_exists_on_table_with_no_options_parameter_supplied
377
+ connection.ddl_batch do
378
+ connection.create_table :testings do |t|
379
+ t.string :foo
380
+ end
381
+ end
382
+ connection.change_table :testings do |t|
383
+ assert t.column_exists?(:foo)
384
+ assert_not (t.column_exists?(:bar))
385
+ end
386
+ end
387
+
388
+ def test_drop_table_if_exists
389
+ connection.ddl_batch do
390
+ connection.create_table(:testings)
391
+ end
392
+ assert connection.table_exists?(:testings)
393
+ connection.ddl_batch do
394
+ connection.drop_table(:testings, if_exists: true)
395
+ end
396
+ assert_not connection.table_exists?(:testings)
397
+ end
398
+
399
+ def test_drop_table_if_exists_nothing_raised
400
+ assert_nothing_raised { connection.drop_table(:nonexistent, if_exists: true) }
401
+ end
402
+
403
+ def test_drop_and_create_table_with_indexes
404
+ connection.ddl_batch do
405
+ connection.create_table :testings, force: true do |t|
406
+ t.string :name, index: true
407
+ end
408
+ end
409
+
410
+ assert_nothing_raised {
411
+ connection.create_table :testings, force: true do |t|
412
+ t.string :name, index: true
413
+ end
414
+ }
415
+
416
+ assert_equal true, connection.table_exists?(:testings)
417
+ assert_equal true, connection.index_exists?(:testings, :name)
418
+ end
419
+
420
+ private
421
+
422
+ def testing_table_with_only_foo_attribute
423
+ connection.ddl_batch do
424
+ connection.create_table :testings do |t|
425
+ t.column :foo, :string
426
+ end
427
+ end
428
+
429
+ yield
430
+ end
431
+ end
432
+ end
433
+ end
@@ -0,0 +1,115 @@
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
+ require "test_helper"
10
+
11
+ module ActiveRecord
12
+ class Migration
13
+ class TableTest < SpannerAdapter::TestCase
14
+ include SpannerAdapter::Migration::TestHelper
15
+
16
+ def setup
17
+ skip_test_table_create!
18
+
19
+ super
20
+ @connection = Minitest::Mock.new
21
+ end
22
+
23
+ def teardown
24
+ assert @connection.verify
25
+ end
26
+
27
+ def with_change_table
28
+ yield ActiveRecord::Base.connection.update_table_definition(:delete_me, @connection)
29
+ end
30
+
31
+ def test_remove_references_column_type_with_polymorphic_removes_type
32
+ with_change_table do |t|
33
+ @connection.expect :remove_reference, nil, [:delete_me, :taggable, polymorphic: true]
34
+ t.remove_references :taggable, polymorphic: true
35
+ end
36
+ end
37
+
38
+ def test_references_column_type_with_polymorphic_and_options_null_is_false_adds_table_flag
39
+ with_change_table do |t|
40
+ @connection.expect :add_reference, nil, [:delete_me, :taggable, polymorphic: true, null: false]
41
+ t.references :taggable, polymorphic: true, null: false
42
+ end
43
+ end
44
+
45
+ def test_remove_references_column_type_with_polymorphic_and_options_null_is_false_removes_table_flag
46
+ with_change_table do |t|
47
+ @connection.expect :remove_reference, nil, [:delete_me, :taggable, polymorphic: true, null: false]
48
+ t.remove_references :taggable, polymorphic: true, null: false
49
+ end
50
+ end
51
+
52
+ def test_references_column_type_with_polymorphic_and_type
53
+ with_change_table do |t|
54
+ @connection.expect :add_reference, nil, [:delete_me, :taggable, polymorphic: true, type: :string]
55
+ t.references :taggable, polymorphic: true, type: :string
56
+ end
57
+ end
58
+
59
+ def test_remove_references_column_type_with_polymorphic_and_type
60
+ with_change_table do |t|
61
+ @connection.expect :remove_reference, nil, [:delete_me, :taggable, polymorphic: true, type: :string]
62
+ t.remove_references :taggable, polymorphic: true, type: :string
63
+ end
64
+ end
65
+
66
+ def test_timestamps_creates_updated_at_and_created_at
67
+ with_change_table do |t|
68
+ @connection.expect :add_timestamps, nil, [:delete_me, null: true]
69
+ t.timestamps null: true
70
+ end
71
+ end
72
+
73
+ def test_remove_timestamps_creates_updated_at_and_created_at
74
+ with_change_table do |t|
75
+ @connection.expect :remove_timestamps, nil, [:delete_me, { null: true }]
76
+ t.remove_timestamps(null: true)
77
+ end
78
+ end
79
+
80
+ def test_primary_key_creates_primary_key_column
81
+ with_change_table do |t|
82
+ @connection.expect :add_column, nil, [:delete_me, :id, :primary_key, primary_key: true, first: true]
83
+ t.primary_key :id, first: true
84
+ end
85
+ end
86
+
87
+ def test_index_exists
88
+ with_change_table do |t|
89
+ @connection.expect :index_exists?, nil, [:delete_me, :bar, {}]
90
+ t.index_exists?(:bar)
91
+ end
92
+ end
93
+
94
+ def test_index_exists_with_options
95
+ with_change_table do |t|
96
+ @connection.expect :index_exists?, nil, [:delete_me, :bar, { unique: true }]
97
+ t.index_exists?(:bar, unique: true)
98
+ end
99
+ end
100
+
101
+ def test_remove_drops_multiple_columns_when_column_options_are_given
102
+ with_change_table do |t|
103
+ @connection.expect :remove_columns, nil, [:delete_me, :bar, :baz, type: :string, null: false]
104
+ t.remove :bar, :baz, type: :string, null: false
105
+ end
106
+ end
107
+
108
+ def test_table_name_set
109
+ with_change_table do |t|
110
+ assert_equal :delete_me, t.name
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,122 @@
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
+ require "test_helper"
10
+ require "bigdecimal"
11
+
12
+ module ActiveRecord
13
+ class Migration
14
+ class ColumnAttributesTest < SpannerAdapter::TestCase
15
+ include SpannerAdapter::Migration::TestHelper
16
+
17
+ def test_add_remove_single_field_using_string_arguments
18
+ assert_no_column TestModel, :last_name
19
+
20
+ add_column "test_models", "last_name", :string
21
+ assert_column TestModel, :last_name
22
+
23
+ remove_column "test_models", "last_name"
24
+ assert_no_column TestModel, :last_name
25
+ end
26
+
27
+ def test_add_remove_single_field_using_symbol_arguments
28
+ assert_no_column TestModel, :last_name
29
+
30
+ add_column :test_models, :last_name, :string
31
+ assert_column TestModel, :last_name
32
+
33
+ remove_column :test_models, :last_name
34
+ assert_no_column TestModel, :last_name
35
+ end
36
+
37
+ def test_add_column_without_limit
38
+ add_column :test_models, :description, :string, limit: nil
39
+ TestModel.reset_column_information
40
+ assert_equal "MAX", TestModel.columns_hash["description"].limit
41
+ end
42
+
43
+ # We specifically do a manual INSERT here, and then test only the SELECT
44
+ # functionality. This allows us to more easily catch INSERT being broken,
45
+ # but SELECT actually working fine.
46
+ def test_native_float_insert_manual_vs_automatic
47
+ correct_value = "0012345678901234567890.0123456789".to_f
48
+
49
+ connection.add_column "test_models", "wealth", :float
50
+
51
+ # Do a manual insertion
52
+ connection.transaction {
53
+ connection.execute "insert into test_models (id, wealth) values (#{generate_id}, 12345678901234567890.0123456789)"
54
+ }
55
+
56
+ # SELECT
57
+ row = TestModel.first
58
+ assert_kind_of Float, row.wealth
59
+
60
+ # If this assert fails, that means the SELECT is broken!
61
+ assert_equal correct_value, row.wealth
62
+
63
+ # Reset to old state
64
+ TestModel.delete_all
65
+
66
+ # Now use the Rails insertion
67
+ TestModel.create wealth: BigDecimal("12345678901234567890.0123456789")
68
+
69
+ # SELECT
70
+ row = TestModel.first
71
+ assert_kind_of Float, row.wealth
72
+
73
+ # If these asserts fail, that means the INSERT (create function, or cast to SQL) is broken!
74
+ assert_equal correct_value, row.wealth
75
+ end
76
+
77
+ def test_native_types
78
+ connection.ddl_batch do
79
+ add_column "test_models", "first_name", :string
80
+ add_column "test_models", "last_name", :string
81
+ add_column "test_models", "bio", :text
82
+ add_column "test_models", "age", :integer
83
+ add_column "test_models", "height", :float
84
+ add_column "test_models", "birthday", :datetime
85
+ add_column "test_models", "favorite_day", :date
86
+ add_column "test_models", "moment_of_truth", :datetime
87
+ add_column "test_models", "male", :boolean
88
+ add_column "test_models", "weight", :decimal
89
+ end
90
+
91
+ TestModel.create first_name: "bob", last_name: "bobsen",
92
+ bio: "I was born ....", age: 18, height: 1.78,
93
+ birthday: 18.years.ago, favorite_day: 10.days.ago,
94
+ moment_of_truth: "1782-10-10 21:40:18", male: true, weight: BigDecimal("75.6", 1)
95
+
96
+ bob = TestModel.first
97
+ assert_equal "bob", bob.first_name
98
+ assert_equal "bobsen", bob.last_name
99
+ assert_equal "I was born ....", bob.bio
100
+ assert_equal 18, bob.age
101
+ assert_equal 1.78, bob.height
102
+ assert_equal true, bob.male?
103
+ assert_equal BigDecimal("75.6", 1), bob.weight
104
+
105
+ assert_equal String, bob.first_name.class
106
+ assert_equal String, bob.last_name.class
107
+ assert_equal String, bob.bio.class
108
+ assert_kind_of Integer, bob.age
109
+ assert_equal Float, bob.height.class
110
+ assert_equal Time, bob.birthday.class
111
+ assert_equal Date, bob.favorite_day.class
112
+ assert_instance_of TrueClass, bob.male?
113
+ assert_equal BigDecimal, bob.weight.class
114
+ end
115
+
116
+ def test_add_column_and_ignore_limit
117
+ add_column :test_models, :integer_ignore_limit, :integer, limit: 10
118
+ assert_column TestModel, :integer_ignore_limit
119
+ end
120
+ end
121
+ end
122
+ end