activerecord-spanner-adapter 1.2.1 → 1.3.1
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.
- checksums.yaml +4 -4
- data/.github/CODEOWNERS +1 -1
- data/.github/blunderbuss.yml +1 -1
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +29 -0
- data/README.md +7 -2
- data/acceptance/cases/migration/schema_dumper_test.rb +69 -0
- data/acceptance/cases/models/generated_column_test.rb +21 -7
- data/acceptance/cases/models/interleave_test.rb +36 -0
- data/acceptance/cases/models/logging_test.rb +53 -0
- data/acceptance/cases/models/query_test.rb +6 -1
- data/acceptance/cases/tasks/database_tasks_test.rb +407 -0
- data/acceptance/models/album_partial_disabled.rb +17 -0
- data/acceptance/schema/schema.rb +139 -134
- data/acceptance/test_helper.rb +2 -0
- data/examples/snippets/array-data-type/db/schema.rb +8 -3
- data/examples/snippets/bulk-insert/db/schema.rb +9 -4
- data/examples/snippets/commit-timestamp/db/schema.rb +11 -6
- data/examples/snippets/create-records/db/schema.rb +9 -4
- data/examples/snippets/date-data-type/db/schema.rb +8 -3
- data/examples/snippets/generated-column/db/schema.rb +6 -1
- data/examples/snippets/hints/db/schema.rb +6 -1
- data/examples/snippets/interleaved-tables/README.md +2 -2
- data/examples/snippets/interleaved-tables/db/schema.rb +5 -0
- data/examples/snippets/migrations/db/schema.rb +10 -5
- data/examples/snippets/mutations/db/schema.rb +9 -4
- data/examples/snippets/optimistic-locking/db/schema.rb +9 -4
- data/examples/snippets/partitioned-dml/db/schema.rb +5 -0
- data/examples/snippets/quickstart/db/schema.rb +9 -4
- data/examples/snippets/read-only-transactions/db/schema.rb +5 -0
- data/examples/snippets/read-write-transactions/db/schema.rb +9 -4
- data/examples/snippets/stale-reads/db/schema.rb +5 -0
- data/examples/snippets/timestamp-data-type/db/schema.rb +8 -3
- data/lib/active_record/connection_adapters/spanner/column.rb +23 -0
- data/lib/active_record/connection_adapters/spanner/database_statements.rb +7 -4
- data/lib/active_record/connection_adapters/spanner/quoting.rb +9 -0
- data/lib/active_record/connection_adapters/spanner/schema_creation.rb +17 -4
- data/lib/active_record/connection_adapters/spanner/schema_definitions.rb +11 -2
- data/lib/active_record/connection_adapters/spanner/schema_dumper.rb +56 -0
- data/lib/active_record/connection_adapters/spanner/schema_statements.rb +56 -9
- data/lib/active_record/connection_adapters/spanner/type_metadata.rb +19 -4
- data/lib/active_record/connection_adapters/spanner_adapter.rb +11 -0
- data/lib/active_record/tasks/spanner_database_tasks.rb +18 -4
- data/lib/active_record/type/spanner/spanner_active_record_converter.rb +10 -0
- data/lib/active_record/type/spanner/time.rb +10 -3
- data/lib/activerecord_spanner_adapter/base.rb +41 -27
- data/lib/activerecord_spanner_adapter/connection.rb +8 -3
- data/lib/activerecord_spanner_adapter/information_schema.rb +52 -3
- data/lib/activerecord_spanner_adapter/table/column.rb +7 -2
- data/lib/activerecord_spanner_adapter/version.rb +1 -1
- data/lib/arel/visitors/spanner.rb +8 -2
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0b6dba891089af7c51b13f3b80ce8342b691d31cd0e31181dbd5799c240a2e9c
|
4
|
+
data.tar.gz: 54a6079aa300d4e31bb8f47630857e624e94995dc713aae60aa848ac3da41ca0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d5f05cd6a79976d974d3d5aa472d514146b5367dae7a4c334913627acc0c98154c8a247c0225a42f235c976b9d3e28cd8c29c46a3c1e876eda6c181619e11617
|
7
|
+
data.tar.gz: b943df790fa40955c31b5e9df89396a84d5f9b0499e8d610758238a249de1cc693b53f87f9b5d134b6595be5677222b04685206b8ca5d28472e6f9fbdac20ad9
|
data/.github/CODEOWNERS
CHANGED
data/.github/blunderbuss.yml
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
assign_issues:
|
2
|
-
-
|
2
|
+
- olavloite
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,34 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
### 1.3.1 (2022-12-15)
|
4
|
+
|
5
|
+
#### Bug Fixes
|
6
|
+
|
7
|
+
* build error for ruby 2.5 ([#216](https://github.com/googleapis/ruby-spanner-activerecord/issues/216))
|
8
|
+
|
9
|
+
### 1.3.0 (2022-12-08)
|
10
|
+
|
11
|
+
#### Features
|
12
|
+
|
13
|
+
* add check constraint support to migrations ([#205](https://github.com/googleapis/ruby-spanner-activerecord/issues/205))
|
14
|
+
* allows passing of type parameter when creating parent_key column ([#195](https://github.com/googleapis/ruby-spanner-activerecord/issues/195))
|
15
|
+
* include index options in the output of SchemaDumper ([#203](https://github.com/googleapis/ruby-spanner-activerecord/issues/203))
|
16
|
+
* schema_dumper should use DDL batch ([#207](https://github.com/googleapis/ruby-spanner-activerecord/issues/207))
|
17
|
+
* support column DEFAULT expressions in migrations ([#196](https://github.com/googleapis/ruby-spanner-activerecord/issues/196))
|
18
|
+
#### Bug Fixes
|
19
|
+
|
20
|
+
* ignore no database when recreating ([#208](https://github.com/googleapis/ruby-spanner-activerecord/issues/208))
|
21
|
+
#### Documentation
|
22
|
+
|
23
|
+
* fix typo in example of interleaved-tables ([#209](https://github.com/googleapis/ruby-spanner-activerecord/issues/209))
|
24
|
+
|
25
|
+
### 1.2.2 (2022-08-29)
|
26
|
+
|
27
|
+
#### Documentation
|
28
|
+
|
29
|
+
* add ActiveRecord 7 as a supported version to the README ([#189](https://github.com/googleapis/ruby-spanner-activerecord/issues/189))
|
30
|
+
* update limitation on interleaved tables and default column values ([#190](https://github.com/googleapis/ruby-spanner-activerecord/issues/190))
|
31
|
+
|
3
32
|
### 1.2.1 (2022-08-28)
|
4
33
|
|
5
34
|
#### Bug Fixes
|
data/README.md
CHANGED
@@ -8,10 +8,14 @@ This project provides a Cloud Spanner adapter for ActiveRecord. It supports the
|
|
8
8
|
|
9
9
|
- ActiveRecord 6.0.x with Ruby 2.6 and 2.7.
|
10
10
|
- ActiveRecord 6.1.x with Ruby 2.6 and higher.
|
11
|
+
- ActiveRecord 7.0.x with Ruby 2.7 and higher.
|
11
12
|
|
12
13
|
Known limitations are listed in the [Limitations](#limitations) section.
|
13
14
|
Please report any problems that you might encounter by [creating a new issue](https://github.com/googleapis/ruby-spanner-activerecord/issues/new).
|
14
15
|
|
16
|
+
This adapter only supports GoogleSQL-dialect Cloud Spanner databases. PostgreSQL-dialect
|
17
|
+
databases are not supported.
|
18
|
+
|
15
19
|
## Installation
|
16
20
|
|
17
21
|
Add this line to your application's Gemfile:
|
@@ -70,18 +74,19 @@ Some noteworthy examples in the snippets directory:
|
|
70
74
|
for inserting, updating and deleting data in a Cloud Spanner database. Mutations can have a significant performance
|
71
75
|
advantage compared to DML statements, but do not allow read-your-writes semantics during a transaction.
|
72
76
|
- [array-data-type](examples/snippets/array-data-type): Shows how to work with `ARRAY` data types.
|
77
|
+
- [interleaved-tables](examples/snippets/interleaved-tables): Shows how to work with [Interleaved Tables](https://cloud.google.com/spanner/docs/schema-and-data-model#create-interleaved-tables).
|
73
78
|
|
74
79
|
## Limitations
|
75
80
|
|
76
81
|
Limitation|Comment|Resolution
|
77
82
|
---|---|---
|
78
|
-
Interleaved tables
|
79
|
-
Lack of DEFAULT for columns [change_column_default](https://apidock.com/rails/v5.2.3/ActiveRecord/ConnectionAdapters/SchemaStatements/change_column_default)|Cloud Spanner does not support DEFAULT values for columns. The use of default must be enforced in your controller logic| Always set a value in your model or controller logic.
|
83
|
+
Interleaved tables require composite primary keys| Cloud Spanner requires composite primary keys for interleaved tables. See {file:examples/snippets/interleaved-tables/README.md this example} for an example on how to use interleaved tables with ActiveRecord |Use composite primary keys.
|
80
84
|
Lack of sequential and auto-assigned IDs|Cloud Spanner doesn't autogenerate IDs and this integration instead creates UUID4 to avoid [hotspotting](https://cloud.google.com/spanner/docs/schema-design#uuid_primary_key) so you SHOULD NOT rely on IDs being sorted| UUID4s are automatically generated for primary keys.
|
81
85
|
Table without Primary Key| Cloud Spanner support does not support tables without a primary key.| Always define a primary key for your table.
|
82
86
|
Table names CANNOT have spaces within them whether back-ticked or not|Cloud Spanner DOES NOT support tables with spaces in them for example `Entity ID`|Ensure that your table names don't contain spaces.
|
83
87
|
Table names CANNOT have punctuation marks and MUST contain valid UTF-8|Cloud Spanner DOES NOT support punctuation marks e.g. periods ".", question marks "?" in table names|Ensure that your table names don't contain punctuation marks.
|
84
88
|
Index with fields length [add_index](https://apidock.com/rails/v5.2.3/ActiveRecord/ConnectionAdapters/SchemaStatements/add_index)|Cloud Spanner does not support index with fields length | Ensure that your database definition does not include index definitions with field lengths.
|
89
|
+
Only GoogleSQL-dialect databases| Cloud Spanner supports both GoogleSQL- and PostgreSQL-dialect databases. This adapter only supports GoogleSQL-dialect databases. | Ensure that your database uses the GoogleSQL dialect.
|
85
90
|
|
86
91
|
## Contributing
|
87
92
|
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# Copyright 2022 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 IndexTest < SpannerAdapter::TestCase
|
14
|
+
include SpannerAdapter::Migration::TestHelper
|
15
|
+
|
16
|
+
def test_dump_schema_contains_start_batch_ddl
|
17
|
+
connection = ActiveRecord::Base.connection
|
18
|
+
schema = StringIO.new
|
19
|
+
ActiveRecord::SchemaDumper.dump connection, schema
|
20
|
+
assert schema.string.include?("connection.start_batch_ddl")
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_dump_schema_contains_run_batch
|
24
|
+
connection = ActiveRecord::Base.connection
|
25
|
+
schema = StringIO.new
|
26
|
+
ActiveRecord::SchemaDumper.dump connection, schema
|
27
|
+
assert schema.string.include?(" connection.run_batch\n"\
|
28
|
+
"rescue\n"\
|
29
|
+
" abort_batch\n"\
|
30
|
+
" raise")
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_dump_schema_contains_albums_table
|
34
|
+
connection = ActiveRecord::Base.connection
|
35
|
+
schema = StringIO.new
|
36
|
+
ActiveRecord::SchemaDumper.dump connection, schema
|
37
|
+
assert schema.string.include?("create_table \"albums\", primary_key: \"albumid\"")
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_dump_schema_contains_interleaved_index
|
41
|
+
connection = ActiveRecord::Base.connection
|
42
|
+
schema = StringIO.new
|
43
|
+
ActiveRecord::SchemaDumper.dump connection, schema
|
44
|
+
assert schema.string.include?("t.index [\"singerid\", \"albumid\", \"title\"], name: \"index_tracks_on_singerid_and_albumid_and_title\", order: { singerid: :asc, albumid: :asc, title: :asc }, null_filtered: true, interleave_in: \"albums\""), schema.string
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_dump_schema_should_not_contain_id_limit
|
48
|
+
connection = ActiveRecord::Base.connection
|
49
|
+
schema = StringIO.new
|
50
|
+
ActiveRecord::SchemaDumper.dump connection, schema
|
51
|
+
assert !schema.string.include?("id: { limit: 8 }")
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_dump_schema_contains_commit_timestamp
|
55
|
+
connection = ActiveRecord::Base.connection
|
56
|
+
schema = StringIO.new
|
57
|
+
ActiveRecord::SchemaDumper.dump connection, schema
|
58
|
+
assert schema.string.include?("t.time \"last_updated\", allow_commit_timestamp: true"), schema.string
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_dump_schema_contains_virtual_column
|
62
|
+
connection = ActiveRecord::Base.connection
|
63
|
+
schema = StringIO.new
|
64
|
+
ActiveRecord::SchemaDumper.dump connection, schema
|
65
|
+
assert schema.string.include?("t.virtual \"full_name\", type: :string, as: \"COALESCE(first_name || ' ', '') || last_name\", stored: true"), schema.string
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -76,13 +76,24 @@ module ActiveRecord
|
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
79
|
+
VERSION_7 = ActiveRecord.gem_version >= Gem::Version.create("7.0.0")
|
80
|
+
|
81
|
+
def assert_raises_below_ar_7(ex, &test)
|
82
|
+
if VERSION_7
|
83
|
+
assert_nothing_raised &test
|
84
|
+
else
|
85
|
+
assert_raises ex, &test
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
79
89
|
def test_create_with_value_for_generated_column
|
80
90
|
# Note: The statement itself will not fail for an explicit transaction that uses buffered transactions.
|
81
91
|
# Instead, the commit will fail. That is tested in a separate test case.
|
82
92
|
[nil, :serializable].each do |isolation|
|
83
93
|
run_in_transaction isolation do
|
84
|
-
|
85
|
-
Singer.create first_name: "Pete", last_name: "Allison", full_name: "Alice Peterson"
|
94
|
+
assert_raises_below_ar_7 ActiveRecord::StatementInvalid do
|
95
|
+
singer = Singer.create first_name: "Pete", last_name: "Allison", full_name: "Alice Peterson"
|
96
|
+
assert_equal "Pete Allison", singer.reload.full_name
|
86
97
|
end
|
87
98
|
end
|
88
99
|
end
|
@@ -90,10 +101,11 @@ module ActiveRecord
|
|
90
101
|
|
91
102
|
def test_create_with_value_for_generated_column_buffered_mutations
|
92
103
|
# The transaction itself will raise an error, as the failure occurs during the commit.
|
93
|
-
|
94
|
-
|
104
|
+
assert_raises_below_ar_7 ActiveRecord::StatementInvalid do
|
105
|
+
singer = run_in_transaction :buffered_mutations do
|
95
106
|
Singer.create first_name: "Pete", last_name: "Allison", full_name: "Alice Peterson"
|
96
107
|
end
|
108
|
+
assert_equal "Pete Allison", singer.reload.full_name
|
97
109
|
end
|
98
110
|
end
|
99
111
|
|
@@ -104,10 +116,11 @@ module ActiveRecord
|
|
104
116
|
singer = Singer.create first_name: "Pete", last_name: "Allison"
|
105
117
|
singer.reload # reload to ensure the full_name attribute is populated.
|
106
118
|
run_in_transaction isolation do
|
107
|
-
|
119
|
+
assert_raises_below_ar_7 ActiveRecord::StatementInvalid do
|
108
120
|
Singer.update full_name: "Alice Peterson"
|
109
121
|
end
|
110
122
|
end
|
123
|
+
assert_equal "Pete Allison", singer.reload.full_name
|
111
124
|
end
|
112
125
|
end
|
113
126
|
|
@@ -115,11 +128,12 @@ module ActiveRecord
|
|
115
128
|
singer = Singer.create first_name: "Pete", last_name: "Allison"
|
116
129
|
singer.reload # reload to ensure the full_name attribute is populated.
|
117
130
|
# The transaction itself will raise an error, as the failure occurs during the commit.
|
118
|
-
|
119
|
-
|
131
|
+
assert_raises_below_ar_7 ActiveRecord::StatementInvalid do
|
132
|
+
run_in_transaction :buffered_mutations do
|
120
133
|
Singer.update full_name: "Alice Peterson"
|
121
134
|
end
|
122
135
|
end
|
136
|
+
assert_equal "Pete Allison", singer.reload.full_name
|
123
137
|
end
|
124
138
|
end
|
125
139
|
end
|
@@ -0,0 +1,36 @@
|
|
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
|
+
# frozen_string_literal: true
|
8
|
+
|
9
|
+
require "test_helper"
|
10
|
+
require "models/singer"
|
11
|
+
require "models/album_partial_disabled"
|
12
|
+
|
13
|
+
module ActiveRecord
|
14
|
+
module Model
|
15
|
+
class InterleaveTest < SpannerAdapter::TestCase
|
16
|
+
include SpannerAdapter::Associations::TestHelper
|
17
|
+
|
18
|
+
attr_accessor :singer
|
19
|
+
|
20
|
+
def setup
|
21
|
+
super
|
22
|
+
|
23
|
+
@singer = Singer.create first_name: "FirstName", last_name: "LastName"
|
24
|
+
end
|
25
|
+
|
26
|
+
def teardown
|
27
|
+
Album.destroy_all
|
28
|
+
Singer.destroy_all
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_with_partial_inserts_disabled
|
32
|
+
AlbumPartialDisabled.create! title: "Title3", singer: singer
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Copyright 2022 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 "active_support/log_subscriber/test_helper"
|
10
|
+
require "test_helper"
|
11
|
+
require "models/post"
|
12
|
+
|
13
|
+
module ActiveRecord
|
14
|
+
module Model
|
15
|
+
class LoggingTest < SpannerAdapter::TestCase
|
16
|
+
include ActiveSupport::LogSubscriber::TestHelper
|
17
|
+
|
18
|
+
setup do
|
19
|
+
ActiveRecord::LogSubscriber.attach_to(:active_record)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_logs_without_binds
|
23
|
+
published_time = Time.new(2016, 05, 11, 19, 0, 0)
|
24
|
+
Post.where(published_time: published_time, title: 'Title - 1').first
|
25
|
+
|
26
|
+
wait
|
27
|
+
assert @logger.logged(:debug).length >= 1
|
28
|
+
assert_no_match "[[\"published_time\", \"#{published_time.utc.iso8601(9)}\"], [\"title\", \"Title - 1\"]",
|
29
|
+
@logger.logged(:debug).last
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_logs_with_binds
|
33
|
+
ActiveRecord::ConnectionAdapters::SpannerAdapter.log_statement_binds = true
|
34
|
+
|
35
|
+
published_time = Time.new(2016, 05, 11, 19, 0, 0)
|
36
|
+
Post.where(published_time: published_time, title: 'Title - 1').first
|
37
|
+
|
38
|
+
wait
|
39
|
+
assert @logger.logged(:debug).length >= 1
|
40
|
+
assert_match "[[\"published_time\", \"#{published_time.utc.iso8601(9)}\"], [\"title\", \"Title - 1\"]",
|
41
|
+
@logger.logged(:debug).last
|
42
|
+
ensure
|
43
|
+
ActiveRecord::ConnectionAdapters::SpannerAdapter.log_statement_binds = false
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def set_logger(logger)
|
49
|
+
ActiveRecord::Base.logger = logger
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -166,6 +166,11 @@ module ActiveRecord
|
|
166
166
|
assert_nil post.id
|
167
167
|
assert_equal "Title - 1", post.title
|
168
168
|
end
|
169
|
+
|
170
|
+
def test_to_sql
|
171
|
+
assert_equal "SELECT `comments`.* FROM `comments` WHERE `comments`.`comment` = 'Comment - 2'",
|
172
|
+
Comment.where(comment: 'Comment - 2').to_sql
|
173
|
+
end
|
169
174
|
end
|
170
175
|
end
|
171
|
-
end
|
176
|
+
end
|