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.
- checksums.yaml +5 -5
- data/.github/CODEOWNERS +7 -0
- data/.github/sync-repo-settings.yaml +16 -0
- data/.github/workflows/acceptance-tests-on-emulator.yaml +45 -0
- data/.github/workflows/acceptance-tests-on-production.yaml +49 -0
- data/.github/workflows/ci.yaml +33 -0
- data/.github/workflows/nightly-acceptance-tests-on-emulator.yaml +52 -0
- data/.github/workflows/nightly-acceptance-tests-on-production.yaml +35 -0
- data/.github/workflows/nightly-unit-tests.yaml +40 -0
- data/.github/workflows/release-please-label.yml +25 -0
- data/.github/workflows/release-please.yml +39 -0
- data/.github/workflows/rubocop.yaml +31 -0
- data/.gitignore +67 -5
- data/.kokoro/populate-secrets.sh +77 -0
- data/.kokoro/release.cfg +33 -0
- data/.kokoro/release.sh +15 -0
- data/.kokoro/trampoline_v2.sh +489 -0
- data/.rubocop.yml +46 -0
- data/.toys/release.rb +18 -0
- data/.trampolinerc +48 -0
- data/.yardopts +11 -0
- data/CHANGELOG.md +55 -0
- data/CODE_OF_CONDUCT.md +40 -0
- data/CONTRIBUTING.md +79 -0
- data/Gemfile +9 -4
- data/LICENSE +6 -6
- data/README.md +66 -30
- data/Rakefile +79 -3
- data/SECURITY.md +7 -0
- data/acceptance/cases/associations/has_many_associations_test.rb +119 -0
- data/acceptance/cases/associations/has_many_through_associations_test.rb +63 -0
- data/acceptance/cases/associations/has_one_associations_test.rb +79 -0
- data/acceptance/cases/associations/has_one_through_associations_test.rb +98 -0
- data/acceptance/cases/interleaved_associations/has_many_associations_using_interleaved_test.rb +211 -0
- data/acceptance/cases/migration/change_schema_test.rb +433 -0
- data/acceptance/cases/migration/change_table_test.rb +115 -0
- data/acceptance/cases/migration/column_attributes_test.rb +122 -0
- data/acceptance/cases/migration/column_positioning_test.rb +48 -0
- data/acceptance/cases/migration/columns_test.rb +201 -0
- data/acceptance/cases/migration/command_recorder_test.rb +406 -0
- data/acceptance/cases/migration/create_join_table_test.rb +216 -0
- data/acceptance/cases/migration/ddl_batching_test.rb +80 -0
- data/acceptance/cases/migration/foreign_key_test.rb +297 -0
- data/acceptance/cases/migration/index_test.rb +211 -0
- data/acceptance/cases/migration/references_foreign_key_test.rb +259 -0
- data/acceptance/cases/migration/references_index_test.rb +135 -0
- data/acceptance/cases/migration/references_statements_test.rb +166 -0
- data/acceptance/cases/migration/rename_column_test.rb +96 -0
- data/acceptance/cases/models/calculation_query_test.rb +128 -0
- data/acceptance/cases/models/generated_column_test.rb +126 -0
- data/acceptance/cases/models/mutation_test.rb +122 -0
- data/acceptance/cases/models/query_test.rb +171 -0
- data/acceptance/cases/sessions/session_not_found_test.rb +121 -0
- data/acceptance/cases/transactions/optimistic_locking_test.rb +141 -0
- data/acceptance/cases/transactions/read_only_transactions_test.rb +130 -0
- data/acceptance/cases/transactions/read_write_transactions_test.rb +248 -0
- data/acceptance/cases/type/all_types_test.rb +172 -0
- data/acceptance/cases/type/binary_test.rb +59 -0
- data/acceptance/cases/type/boolean_test.rb +31 -0
- data/acceptance/cases/type/date_test.rb +32 -0
- data/acceptance/cases/type/date_time_test.rb +30 -0
- data/acceptance/cases/type/float_test.rb +27 -0
- data/acceptance/cases/type/integer_test.rb +44 -0
- data/acceptance/cases/type/json_test.rb +34 -0
- data/acceptance/cases/type/numeric_test.rb +27 -0
- data/acceptance/cases/type/string_test.rb +79 -0
- data/acceptance/cases/type/text_test.rb +30 -0
- data/acceptance/cases/type/time_test.rb +87 -0
- data/acceptance/models/account.rb +13 -0
- data/acceptance/models/address.rb +9 -0
- data/acceptance/models/album.rb +12 -0
- data/acceptance/models/all_types.rb +8 -0
- data/acceptance/models/author.rb +11 -0
- data/acceptance/models/club.rb +12 -0
- data/acceptance/models/comment.rb +9 -0
- data/acceptance/models/customer.rb +9 -0
- data/acceptance/models/department.rb +9 -0
- data/acceptance/models/firm.rb +10 -0
- data/acceptance/models/member.rb +13 -0
- data/acceptance/models/member_type.rb +9 -0
- data/acceptance/models/membership.rb +10 -0
- data/acceptance/models/organization.rb +9 -0
- data/acceptance/models/post.rb +10 -0
- data/acceptance/models/singer.rb +10 -0
- data/acceptance/models/track.rb +20 -0
- data/acceptance/models/transaction.rb +9 -0
- data/acceptance/schema/schema.rb +147 -0
- data/acceptance/test_helper.rb +261 -0
- data/activerecord-spanner-adapter.gemspec +32 -17
- data/assets/solidus-db.png +0 -0
- data/benchmarks/README.md +17 -0
- data/benchmarks/Rakefile +14 -0
- data/benchmarks/application.rb +308 -0
- data/benchmarks/config/database.yml +8 -0
- data/benchmarks/config/environment.rb +12 -0
- data/benchmarks/db/migrate/01_create_tables.rb +25 -0
- data/benchmarks/db/schema.rb +29 -0
- data/benchmarks/models/album.rb +9 -0
- data/benchmarks/models/singer.rb +9 -0
- data/bin/console +6 -7
- data/examples/rails/README.md +262 -0
- data/examples/snippets/README.md +29 -0
- data/examples/snippets/Rakefile +57 -0
- data/examples/snippets/array-data-type/README.md +45 -0
- data/examples/snippets/array-data-type/Rakefile +13 -0
- data/examples/snippets/array-data-type/application.rb +45 -0
- data/examples/snippets/array-data-type/config/database.yml +8 -0
- data/examples/snippets/array-data-type/db/migrate/01_create_tables.rb +24 -0
- data/examples/snippets/array-data-type/db/schema.rb +26 -0
- data/examples/snippets/array-data-type/db/seeds.rb +5 -0
- data/examples/snippets/array-data-type/models/entity_with_array_types.rb +18 -0
- data/examples/snippets/bin/create_emulator_instance.rb +18 -0
- data/examples/snippets/bulk-insert/README.md +21 -0
- data/examples/snippets/bulk-insert/Rakefile +13 -0
- data/examples/snippets/bulk-insert/application.rb +64 -0
- data/examples/snippets/bulk-insert/config/database.yml +8 -0
- data/examples/snippets/bulk-insert/db/migrate/01_create_tables.rb +21 -0
- data/examples/snippets/bulk-insert/db/schema.rb +26 -0
- data/examples/snippets/bulk-insert/db/seeds.rb +5 -0
- data/examples/snippets/bulk-insert/models/album.rb +9 -0
- data/examples/snippets/bulk-insert/models/singer.rb +9 -0
- data/examples/snippets/commit-timestamp/README.md +18 -0
- data/examples/snippets/commit-timestamp/Rakefile +13 -0
- data/examples/snippets/commit-timestamp/application.rb +53 -0
- data/examples/snippets/commit-timestamp/config/database.yml +8 -0
- data/examples/snippets/commit-timestamp/db/migrate/01_create_tables.rb +26 -0
- data/examples/snippets/commit-timestamp/db/schema.rb +29 -0
- data/examples/snippets/commit-timestamp/db/seeds.rb +5 -0
- data/examples/snippets/commit-timestamp/models/album.rb +9 -0
- data/examples/snippets/commit-timestamp/models/singer.rb +9 -0
- data/examples/snippets/config/environment.rb +21 -0
- data/examples/snippets/create-records/README.md +12 -0
- data/examples/snippets/create-records/Rakefile +13 -0
- data/examples/snippets/create-records/application.rb +42 -0
- data/examples/snippets/create-records/config/database.yml +8 -0
- data/examples/snippets/create-records/db/migrate/01_create_tables.rb +21 -0
- data/examples/snippets/create-records/db/schema.rb +26 -0
- data/examples/snippets/create-records/db/seeds.rb +5 -0
- data/examples/snippets/create-records/models/album.rb +9 -0
- data/examples/snippets/create-records/models/singer.rb +9 -0
- data/examples/snippets/date-data-type/README.md +19 -0
- data/examples/snippets/date-data-type/Rakefile +13 -0
- data/examples/snippets/date-data-type/application.rb +35 -0
- data/examples/snippets/date-data-type/config/database.yml +8 -0
- data/examples/snippets/date-data-type/db/migrate/01_create_tables.rb +20 -0
- data/examples/snippets/date-data-type/db/schema.rb +21 -0
- data/examples/snippets/date-data-type/db/seeds.rb +16 -0
- data/examples/snippets/date-data-type/models/singer.rb +8 -0
- data/examples/snippets/generated-column/README.md +41 -0
- data/examples/snippets/generated-column/Rakefile +13 -0
- data/examples/snippets/generated-column/application.rb +37 -0
- data/examples/snippets/generated-column/config/database.yml +8 -0
- data/examples/snippets/generated-column/db/migrate/01_create_tables.rb +23 -0
- data/examples/snippets/generated-column/db/schema.rb +21 -0
- data/examples/snippets/generated-column/db/seeds.rb +18 -0
- data/examples/snippets/generated-column/models/singer.rb +8 -0
- data/examples/snippets/hints/README.md +19 -0
- data/examples/snippets/hints/Rakefile +13 -0
- data/examples/snippets/hints/application.rb +47 -0
- data/examples/snippets/hints/config/database.yml +8 -0
- data/examples/snippets/hints/db/migrate/01_create_tables.rb +23 -0
- data/examples/snippets/hints/db/schema.rb +28 -0
- data/examples/snippets/hints/db/seeds.rb +29 -0
- data/examples/snippets/hints/models/album.rb +9 -0
- data/examples/snippets/hints/models/singer.rb +9 -0
- data/examples/snippets/migrations/README.md +43 -0
- data/examples/snippets/migrations/Rakefile +13 -0
- data/examples/snippets/migrations/application.rb +26 -0
- data/examples/snippets/migrations/config/database.yml +8 -0
- data/examples/snippets/migrations/db/migrate/01_create_tables.rb +28 -0
- data/examples/snippets/migrations/db/schema.rb +33 -0
- data/examples/snippets/migrations/db/seeds.rb +5 -0
- data/examples/snippets/migrations/models/album.rb +10 -0
- data/examples/snippets/migrations/models/singer.rb +10 -0
- data/examples/snippets/migrations/models/track.rb +9 -0
- data/examples/snippets/mutations/README.md +34 -0
- data/examples/snippets/mutations/Rakefile +13 -0
- data/examples/snippets/mutations/application.rb +47 -0
- data/examples/snippets/mutations/config/database.yml +8 -0
- data/examples/snippets/mutations/db/migrate/01_create_tables.rb +22 -0
- data/examples/snippets/mutations/db/schema.rb +27 -0
- data/examples/snippets/mutations/db/seeds.rb +25 -0
- data/examples/snippets/mutations/models/album.rb +9 -0
- data/examples/snippets/mutations/models/singer.rb +9 -0
- data/examples/snippets/optimistic-locking/README.md +12 -0
- data/examples/snippets/optimistic-locking/Rakefile +13 -0
- data/examples/snippets/optimistic-locking/application.rb +48 -0
- data/examples/snippets/optimistic-locking/config/database.yml +8 -0
- data/examples/snippets/optimistic-locking/db/migrate/01_create_tables.rb +26 -0
- data/examples/snippets/optimistic-locking/db/schema.rb +29 -0
- data/examples/snippets/optimistic-locking/db/seeds.rb +25 -0
- data/examples/snippets/optimistic-locking/models/album.rb +9 -0
- data/examples/snippets/optimistic-locking/models/singer.rb +9 -0
- data/examples/snippets/partitioned-dml/README.md +16 -0
- data/examples/snippets/partitioned-dml/Rakefile +13 -0
- data/examples/snippets/partitioned-dml/application.rb +48 -0
- data/examples/snippets/partitioned-dml/config/database.yml +8 -0
- data/examples/snippets/partitioned-dml/db/migrate/01_create_tables.rb +21 -0
- data/examples/snippets/partitioned-dml/db/schema.rb +26 -0
- data/examples/snippets/partitioned-dml/db/seeds.rb +29 -0
- data/examples/snippets/partitioned-dml/models/album.rb +9 -0
- data/examples/snippets/partitioned-dml/models/singer.rb +9 -0
- data/examples/snippets/quickstart/README.md +26 -0
- data/examples/snippets/quickstart/Rakefile +13 -0
- data/examples/snippets/quickstart/application.rb +51 -0
- data/examples/snippets/quickstart/config/database.yml +8 -0
- data/examples/snippets/quickstart/db/migrate/01_create_tables.rb +21 -0
- data/examples/snippets/quickstart/db/schema.rb +26 -0
- data/examples/snippets/quickstart/db/seeds.rb +24 -0
- data/examples/snippets/quickstart/models/album.rb +9 -0
- data/examples/snippets/quickstart/models/singer.rb +9 -0
- data/examples/snippets/read-only-transactions/README.md +13 -0
- data/examples/snippets/read-only-transactions/Rakefile +13 -0
- data/examples/snippets/read-only-transactions/application.rb +77 -0
- data/examples/snippets/read-only-transactions/config/database.yml +8 -0
- data/examples/snippets/read-only-transactions/db/migrate/01_create_tables.rb +21 -0
- data/examples/snippets/read-only-transactions/db/schema.rb +26 -0
- data/examples/snippets/read-only-transactions/db/seeds.rb +24 -0
- data/examples/snippets/read-only-transactions/models/album.rb +9 -0
- data/examples/snippets/read-only-transactions/models/singer.rb +9 -0
- data/examples/snippets/read-write-transactions/README.md +12 -0
- data/examples/snippets/read-write-transactions/Rakefile +13 -0
- data/examples/snippets/read-write-transactions/application.rb +39 -0
- data/examples/snippets/read-write-transactions/config/database.yml +8 -0
- data/examples/snippets/read-write-transactions/db/migrate/01_create_tables.rb +22 -0
- data/examples/snippets/read-write-transactions/db/schema.rb +27 -0
- data/examples/snippets/read-write-transactions/db/seeds.rb +25 -0
- data/examples/snippets/read-write-transactions/models/album.rb +9 -0
- data/examples/snippets/read-write-transactions/models/singer.rb +9 -0
- data/examples/snippets/stale-reads/README.md +27 -0
- data/examples/snippets/stale-reads/Rakefile +13 -0
- data/examples/snippets/stale-reads/application.rb +63 -0
- data/examples/snippets/stale-reads/config/database.yml +8 -0
- data/examples/snippets/stale-reads/db/migrate/01_create_tables.rb +21 -0
- data/examples/snippets/stale-reads/db/schema.rb +26 -0
- data/examples/snippets/stale-reads/db/seeds.rb +24 -0
- data/examples/snippets/stale-reads/models/album.rb +9 -0
- data/examples/snippets/stale-reads/models/singer.rb +9 -0
- data/examples/snippets/timestamp-data-type/README.md +17 -0
- data/examples/snippets/timestamp-data-type/Rakefile +13 -0
- data/examples/snippets/timestamp-data-type/application.rb +42 -0
- data/examples/snippets/timestamp-data-type/config/database.yml +8 -0
- data/examples/snippets/timestamp-data-type/db/migrate/01_create_tables.rb +21 -0
- data/examples/snippets/timestamp-data-type/db/schema.rb +21 -0
- data/examples/snippets/timestamp-data-type/db/seeds.rb +6 -0
- data/examples/snippets/timestamp-data-type/models/meeting.rb +19 -0
- data/examples/solidus/README.md +172 -0
- data/lib/active_record/connection_adapters/spanner/database_statements.rb +244 -266
- data/lib/active_record/connection_adapters/spanner/quoting.rb +42 -50
- data/lib/active_record/connection_adapters/spanner/schema_cache.rb +43 -0
- data/lib/active_record/connection_adapters/spanner/schema_creation.rb +125 -9
- data/lib/active_record/connection_adapters/spanner/schema_definitions.rb +122 -0
- data/lib/active_record/connection_adapters/spanner/schema_dumper.rb +19 -0
- data/lib/active_record/connection_adapters/spanner/schema_statements.rb +553 -139
- data/lib/active_record/connection_adapters/spanner/type_metadata.rb +37 -0
- data/lib/active_record/connection_adapters/spanner_adapter.rb +185 -78
- data/lib/active_record/tasks/spanner_database_tasks.rb +74 -0
- data/lib/active_record/type/spanner/array.rb +32 -0
- data/lib/active_record/type/spanner/bytes.rb +26 -0
- data/lib/active_record/type/spanner/spanner_active_record_converter.rb +33 -0
- data/lib/active_record/type/spanner/time.rb +37 -0
- data/lib/activerecord-spanner-adapter.rb +23 -0
- data/lib/activerecord_spanner_adapter/base.rb +238 -0
- data/lib/activerecord_spanner_adapter/connection.rb +350 -0
- data/lib/activerecord_spanner_adapter/errors.rb +13 -0
- data/lib/activerecord_spanner_adapter/foreign_key.rb +29 -0
- data/lib/activerecord_spanner_adapter/index/column.rb +38 -0
- data/lib/activerecord_spanner_adapter/index.rb +80 -0
- data/lib/activerecord_spanner_adapter/information_schema.rb +262 -0
- data/lib/activerecord_spanner_adapter/primary_key.rb +31 -0
- data/lib/activerecord_spanner_adapter/table/column.rb +59 -0
- data/lib/activerecord_spanner_adapter/table.rb +61 -0
- data/lib/activerecord_spanner_adapter/transaction.rb +154 -0
- data/lib/activerecord_spanner_adapter/version.rb +9 -0
- data/lib/arel/visitors/spanner.rb +111 -0
- data/lib/spanner_client_ext.rb +107 -0
- data/renovate.json +5 -0
- metadata +405 -34
- data/.travis.yml +0 -5
- data/lib/active_record/connection_adapters/spanner/client.rb +0 -190
- data/lib/active_record/connection_adapters/spanner.rb +0 -10
- data/lib/activerecord-spanner-adapter/version.rb +0 -3
|
@@ -1,66 +1,58 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
1
|
+
# The MIT License (MIT)
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2020 Google LLC.
|
|
4
|
+
#
|
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
# furnished to do so, subject to the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
|
13
|
+
# all copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
# ITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
# THE SOFTWARE.
|
|
22
|
+
|
|
23
|
+
# Copyright 2020 Google LLC
|
|
24
|
+
#
|
|
25
|
+
# Use of this source code is governed by an MIT-style
|
|
26
|
+
# license that can be found in the LICENSE file or at
|
|
27
|
+
# https://opensource.org/licenses/MIT.
|
|
28
|
+
|
|
29
|
+
# frozen_string_literal: true
|
|
3
30
|
|
|
4
31
|
module ActiveRecord
|
|
5
32
|
module ConnectionAdapters
|
|
6
33
|
module Spanner
|
|
7
34
|
module Quoting
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def quote_identifier(name)
|
|
11
|
-
# https://cloud.google.com/spanner/docs/data-definition-language?hl=ja#ddl_syntax
|
|
12
|
-
# raise ArgumentError, "invalid table name #{name}" unless IDENTIFIERS_PATTERN =~ name
|
|
13
|
-
"`#{name}`"
|
|
35
|
+
def quote_column_name name
|
|
36
|
+
self.class.quoted_column_names[name] ||= "`#{super.gsub '`', '``'}`".freeze
|
|
14
37
|
end
|
|
15
38
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
private
|
|
20
|
-
def _type_cast(value)
|
|
21
|
-
# NOTE: Spanner APIs are strongly typed unlike typical SQL interfaces.
|
|
22
|
-
# So we don't want to serialize the value into string unlike other adapters.
|
|
23
|
-
case value
|
|
24
|
-
when Symbol, ActiveSupport::Multibyte::Chars, Type::Binary::Data
|
|
25
|
-
value.to_s
|
|
26
|
-
else
|
|
27
|
-
value
|
|
28
|
-
end
|
|
39
|
+
def quote_table_name name
|
|
40
|
+
self.class.quoted_table_names[name] ||= super.gsub(".", "`.`").freeze
|
|
29
41
|
end
|
|
30
42
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
when true
|
|
36
|
-
quoted_true
|
|
37
|
-
when false
|
|
38
|
-
quoted_false
|
|
39
|
-
when nil
|
|
40
|
-
'NULL'
|
|
41
|
-
when Numeric, ActiveSupport::Duration
|
|
42
|
-
value.to_s
|
|
43
|
-
when Type::Time::Value
|
|
44
|
-
%Q["#{quoted_time(value)}"]
|
|
45
|
-
when Date, Time
|
|
46
|
-
%Q["#{quoted_date(value)}"]
|
|
47
|
-
else
|
|
48
|
-
raise TypeError, "can't quote #{value.class.name}"
|
|
49
|
-
end
|
|
50
|
-
end
|
|
43
|
+
STR_ESCAPE_REGX = /[\n\r'\\]/.freeze
|
|
44
|
+
STR_ESCAPE_VALUES = {
|
|
45
|
+
"\n" => "\\n", "\r" => "\\r", "'" => "\\'", "\\" => "\\\\"
|
|
46
|
+
}.freeze
|
|
51
47
|
|
|
52
|
-
|
|
53
|
-
# Not sure but string-escape syntax in SELECT statements in Spanner
|
|
54
|
-
# looks to be the one in JSON by observation.
|
|
55
|
-
JSON.generate(value)
|
|
56
|
-
end
|
|
48
|
+
private_constant :STR_ESCAPE_REGX, :STR_ESCAPE_VALUES
|
|
57
49
|
|
|
58
|
-
def
|
|
59
|
-
|
|
50
|
+
def quote_string s
|
|
51
|
+
s.gsub STR_ESCAPE_REGX, STR_ESCAPE_VALUES
|
|
60
52
|
end
|
|
61
53
|
|
|
62
|
-
def
|
|
63
|
-
'
|
|
54
|
+
def quoted_binary value
|
|
55
|
+
"b'#{value}'"
|
|
64
56
|
end
|
|
65
57
|
end
|
|
66
58
|
end
|
|
@@ -0,0 +1,43 @@
|
|
|
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
|
+
module ActiveRecord
|
|
8
|
+
module ConnectionAdapters
|
|
9
|
+
class SpannerSchemaCache < SchemaCache
|
|
10
|
+
def initialize conn
|
|
11
|
+
@primary_and_parent_keys = {}
|
|
12
|
+
super
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def initialize_dup other
|
|
16
|
+
@primary_and_parent_keys = @primary_and_parent_keys.dup
|
|
17
|
+
super
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def encode_with coder
|
|
21
|
+
coder["primary_and_parent_keys"] = @primary_and_parent_keys
|
|
22
|
+
super
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def init_with coder
|
|
26
|
+
@primary_and_parent_keys = coder["primary_and_parent_keys"]
|
|
27
|
+
super
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def primary_and_parent_keys table_name
|
|
31
|
+
@primary_and_parent_keys[table_name] ||=
|
|
32
|
+
if data_source_exists? table_name
|
|
33
|
+
connection.primary_and_parent_keys table_name
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def clear!
|
|
38
|
+
@primary_and_parent_keys.clear
|
|
39
|
+
super
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -1,22 +1,138 @@
|
|
|
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
|
+
|
|
1
7
|
module ActiveRecord
|
|
2
8
|
module ConnectionAdapters
|
|
3
9
|
module Spanner
|
|
4
|
-
class
|
|
10
|
+
class SchemaCreation < SchemaCreation
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
# rubocop:disable Naming/MethodName, Metrics/AbcSize
|
|
14
|
+
|
|
15
|
+
def visit_TableDefinition o
|
|
16
|
+
create_sql = +"CREATE TABLE #{quote_table_name o.name} "
|
|
17
|
+
statements = o.columns.map { |c| accept c }
|
|
18
|
+
|
|
19
|
+
o.foreign_keys.each do |to_table, options|
|
|
20
|
+
statements << foreign_key_in_create(o.name, to_table, options)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
create_sql << "(#{statements.join ', '}) " if statements.any?
|
|
24
|
+
|
|
25
|
+
primary_keys = if o.primary_keys
|
|
26
|
+
o.primary_keys
|
|
27
|
+
else
|
|
28
|
+
pk_names = o.columns.each_with_object [] do |c, r|
|
|
29
|
+
if c.type == :primary_key || c.primary_key?
|
|
30
|
+
r << c.name
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
PrimaryKeyDefinition.new pk_names
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
if o.interleave_in?
|
|
37
|
+
parent_names = o.columns.each_with_object [] do |c, r|
|
|
38
|
+
if c.type == :parent_key
|
|
39
|
+
r << c.name
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
primary_keys.name = parent_names.concat primary_keys.name
|
|
43
|
+
create_sql << accept(primary_keys)
|
|
44
|
+
create_sql << ", INTERLEAVE IN PARENT #{quote_table_name o.interleave_in_parent}"
|
|
45
|
+
create_sql << " ON DELETE #{o.on_delete}" if o.on_delete
|
|
46
|
+
else
|
|
47
|
+
create_sql << accept(primary_keys)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
create_sql
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def visit_DropTableDefinition o
|
|
54
|
+
"DROP TABLE #{quote_table_name o.name}"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def visit_ColumnDefinition o
|
|
58
|
+
o.sql_type = type_to_sql o.type, **o.options
|
|
59
|
+
column_sql = +"#{quote_column_name o.name} #{o.sql_type}"
|
|
60
|
+
add_column_options! column_sql, column_options(o)
|
|
61
|
+
column_sql
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def visit_AddColumnDefinition o
|
|
65
|
+
# Overridden to add the optional COLUMN keyword. The keyword is only optional
|
|
66
|
+
# on real Cloud Spanner, the emulator requires the COLUMN keyword to be included.
|
|
67
|
+
+"ADD COLUMN #{accept o.column}"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def visit_DropColumnDefinition o
|
|
71
|
+
"ALTER TABLE #{quote_table_name o.table_name} DROP" \
|
|
72
|
+
" COLUMN #{quote_column_name o.name}"
|
|
73
|
+
end
|
|
5
74
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
DDL.new(ddl)
|
|
75
|
+
def visit_ChangeColumnDefinition o
|
|
76
|
+
sql = +"ALTER TABLE #{quote_table_name o.table_name} ALTER COLUMN "
|
|
77
|
+
sql << accept(o.column)
|
|
78
|
+
sql
|
|
11
79
|
end
|
|
12
80
|
|
|
13
|
-
def
|
|
14
|
-
|
|
81
|
+
def visit_DropIndexDefinition o
|
|
82
|
+
"DROP INDEX #{quote_table_name o.name}"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def visit_IndexDefinition o
|
|
86
|
+
sql = +"CREATE"
|
|
87
|
+
sql << " UNIQUE" if o.unique
|
|
88
|
+
sql << " NULL_FILTERED" if o.null_filtered
|
|
89
|
+
sql << " INDEX #{quote_table_name o.name} "
|
|
90
|
+
|
|
91
|
+
columns_sql = o.columns_with_order.map do |c, order|
|
|
92
|
+
order_sql = +quote_column_name(c)
|
|
93
|
+
order_sql << " DESC" if order == "DESC"
|
|
94
|
+
order_sql
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
sql << "ON #{quote_table_name o.table} (#{columns_sql.join ', '})"
|
|
98
|
+
|
|
99
|
+
if o.storing.any?
|
|
100
|
+
storing = o.storing.map { |s| quote_column_name s }
|
|
101
|
+
sql << " STORING (#{storing.join ', '})"
|
|
102
|
+
end
|
|
103
|
+
if o.interleave_in
|
|
104
|
+
sql << ", INTERLEAVE IN #{quote_table_name o.interleave_in}"
|
|
105
|
+
end
|
|
106
|
+
sql
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# rubocop:enable Naming/MethodName, Metrics/AbcSize
|
|
110
|
+
|
|
111
|
+
def add_column_options! sql, options
|
|
112
|
+
if options[:null] == false || options[:primary_key] == true
|
|
15
113
|
sql << " NOT NULL"
|
|
16
114
|
end
|
|
115
|
+
|
|
116
|
+
if !options[:allow_commit_timestamp].nil? &&
|
|
117
|
+
options[:column].sql_type == "TIMESTAMP"
|
|
118
|
+
sql << " OPTIONS (allow_commit_timestamp = "\
|
|
119
|
+
"#{options[:allow_commit_timestamp]})"
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
if (as = options[:as])
|
|
123
|
+
sql << " AS (#{as})"
|
|
124
|
+
|
|
125
|
+
sql << " STORED" if options[:stored]
|
|
126
|
+
unless options[:stored]
|
|
127
|
+
raise ArgumentError, "" \
|
|
128
|
+
"Cloud Spanner currently does not support generated columns without the STORED option." \
|
|
129
|
+
"Specify 'stored: true' option for `#{options[:column].name}`"
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
sql
|
|
17
134
|
end
|
|
18
135
|
end
|
|
19
136
|
end
|
|
20
137
|
end
|
|
21
138
|
end
|
|
22
|
-
|
|
@@ -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
|
+
module ActiveRecord
|
|
8
|
+
module ConnectionAdapters #:nodoc:
|
|
9
|
+
module Spanner
|
|
10
|
+
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
|
|
11
|
+
attr_reader :interleave_in_parent
|
|
12
|
+
|
|
13
|
+
def interleave_in parent, on_delete = nil
|
|
14
|
+
@interleave_in_parent = parent
|
|
15
|
+
@on_delete = on_delete
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def parent_key name
|
|
19
|
+
column name, :parent_key, null: false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def interleave_in?
|
|
23
|
+
@interleave_in_parent != nil
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def on_delete
|
|
27
|
+
"CASCADE" if @on_delete == :cascade
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def references *args, **options
|
|
31
|
+
args.each do |ref_name|
|
|
32
|
+
Spanner::ReferenceDefinition.new(ref_name, **options).add_to(self)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
alias belongs_to references
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class Table < ActiveRecord::ConnectionAdapters::Table
|
|
39
|
+
def primary_key name, type = :primary_key, **options
|
|
40
|
+
type = :string # rubocop:disable Lint/ShadowedArgument
|
|
41
|
+
options.merge primary_key: true
|
|
42
|
+
super
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
DropTableDefinition = Struct.new :name, :options
|
|
47
|
+
DropColumnDefinition = Struct.new :table_name, :name
|
|
48
|
+
ChangeColumnDefinition = Struct.new :table_name, :column, :name
|
|
49
|
+
DropIndexDefinition = Struct.new :name
|
|
50
|
+
|
|
51
|
+
class ReferenceDefinition < ActiveRecord::ConnectionAdapters::ReferenceDefinition
|
|
52
|
+
def initialize \
|
|
53
|
+
name,
|
|
54
|
+
polymorphic: false,
|
|
55
|
+
index: true,
|
|
56
|
+
foreign_key: false,
|
|
57
|
+
type: :integer,
|
|
58
|
+
**options
|
|
59
|
+
@name = name
|
|
60
|
+
@polymorphic = polymorphic
|
|
61
|
+
@foreign_key = foreign_key
|
|
62
|
+
# Only add an index if there is no foreign key, as Cloud Spanner will automatically add a managed index when
|
|
63
|
+
# a foreign key is added.
|
|
64
|
+
@index = index unless foreign_key
|
|
65
|
+
@type = type
|
|
66
|
+
@options = options
|
|
67
|
+
|
|
68
|
+
return unless polymorphic && foreign_key
|
|
69
|
+
raise ArgumentError, "Cannot add a foreign key to a polymorphic relation"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def columns
|
|
75
|
+
result = [[column_name, type, options]]
|
|
76
|
+
|
|
77
|
+
if polymorphic
|
|
78
|
+
type_options = polymorphic_options.merge limit: 255
|
|
79
|
+
result.unshift ["#{name}_type", :string, type_options]
|
|
80
|
+
end
|
|
81
|
+
result
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
class IndexDefinition < ActiveRecord::ConnectionAdapters::IndexDefinition
|
|
86
|
+
attr_reader :null_filtered, :interleave_in, :storing, :orders
|
|
87
|
+
|
|
88
|
+
def initialize \
|
|
89
|
+
table_name,
|
|
90
|
+
name,
|
|
91
|
+
columns,
|
|
92
|
+
unique: false,
|
|
93
|
+
null_filtered: false,
|
|
94
|
+
interleave_in: nil,
|
|
95
|
+
storing: nil,
|
|
96
|
+
orders: nil
|
|
97
|
+
@table = table_name
|
|
98
|
+
@name = name
|
|
99
|
+
@unique = unique
|
|
100
|
+
@null_filtered = null_filtered
|
|
101
|
+
@interleave_in = interleave_in
|
|
102
|
+
@storing = Array(storing)
|
|
103
|
+
columns = columns.split(/\W/) if columns.is_a? String
|
|
104
|
+
@columns = Array(columns).map(&:to_s)
|
|
105
|
+
@orders = orders || {}
|
|
106
|
+
|
|
107
|
+
unless @orders.is_a? Hash
|
|
108
|
+
@orders = columns.each_with_object({}) { |c, r| r[c] = orders }
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
@orders = @orders.symbolize_keys
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def columns_with_order
|
|
115
|
+
columns.each_with_object({}) do |c, result|
|
|
116
|
+
result[c] = orders[c.to_sym].to_s.upcase
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
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
|
+
module ActiveRecord
|
|
10
|
+
module ConnectionAdapters
|
|
11
|
+
module Spanner
|
|
12
|
+
class SchemaDumper < ConnectionAdapters::SchemaDumper # :nodoc:
|
|
13
|
+
def default_primary_key? column
|
|
14
|
+
schema_type(column) == :integer
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|