activerecord-spanner-adapter 0.3.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,173 +1,587 @@
|
|
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 "active_record/connection_adapters/spanner/schema_creation"
|
10
|
+
require "active_record/connection_adapters/spanner/schema_dumper"
|
11
|
+
|
1
12
|
module ActiveRecord
|
2
13
|
module ConnectionAdapters
|
3
14
|
module Spanner
|
15
|
+
#
|
16
|
+
# # SchemaStatements
|
17
|
+
#
|
18
|
+
# Collection of methods to handle database schema.
|
19
|
+
#
|
20
|
+
# [Schema Doc](https://cloud.google.com/spanner/docs/information-schema)
|
21
|
+
#
|
4
22
|
module SchemaStatements
|
5
|
-
|
6
|
-
|
7
|
-
NATIVE_DATABASE_TYPES = {
|
8
|
-
primary_key: 'STRING(36)',
|
9
|
-
string: { name: 'STRING', limit: 255 },
|
10
|
-
text: { name: 'STRING', limit: 'MAX' },
|
11
|
-
integer: { name: 'INT64' },
|
12
|
-
float: { name: 'FLOAT64' },
|
13
|
-
datetime: { name: 'TIMESTAMP' },
|
14
|
-
date: { name: 'DATE' },
|
15
|
-
binary: { name: 'BYTES', limit: 'MAX' },
|
16
|
-
boolean: { name: 'BOOL' },
|
17
|
-
}
|
18
|
-
|
19
|
-
def native_database_types # :nodoc:
|
20
|
-
NATIVE_DATABASE_TYPES
|
21
|
-
end
|
22
|
-
|
23
|
-
def tables
|
24
|
-
# https://cloud.google.com/spanner/docs/information-schema
|
25
|
-
select_values(<<-SQL, 'SCHEMA')
|
26
|
-
SELECT
|
27
|
-
t.table_name
|
28
|
-
FROM
|
29
|
-
information_schema.tables AS t
|
30
|
-
WHERE
|
31
|
-
t.table_catalog = '' AND t.table_schema = ''
|
32
|
-
SQL
|
33
|
-
end
|
34
|
-
|
35
|
-
def views
|
36
|
-
[]
|
37
|
-
end
|
38
|
-
|
39
|
-
def indexes(table, name = :ignored)
|
40
|
-
params = {table: table}
|
41
|
-
results = exec_query(<<-"SQL", 'SCHEMA', params, prepare: false)
|
42
|
-
SELECT
|
43
|
-
idx.index_name,
|
44
|
-
idx.index_type,
|
45
|
-
idx.parent_table_name,
|
46
|
-
idx.is_unique,
|
47
|
-
idx.is_null_filtered
|
48
|
-
FROM
|
49
|
-
information_schema.indexes AS idx
|
50
|
-
WHERE
|
51
|
-
idx.table_catalog = '' AND
|
52
|
-
idx.table_schema = '' AND
|
53
|
-
idx.table_name = @table
|
54
|
-
SQL
|
55
|
-
|
56
|
-
results.map do |row|
|
57
|
-
col_params = { table: table, index: row['index_name'] }
|
58
|
-
col_results = exec_query(<<-"SQL", 'SCHEMA', col_params, prepare: false)
|
59
|
-
SELECT
|
60
|
-
col.column_name,
|
61
|
-
col.column_ordering
|
62
|
-
FROM
|
63
|
-
information_schema.index_columns AS col
|
64
|
-
WHERE
|
65
|
-
col.table_catalog = '' AND
|
66
|
-
col.table_schema = '' AND
|
67
|
-
col.table_name = @table AND
|
68
|
-
col.index_name = @index
|
69
|
-
ORDER BY
|
70
|
-
col.ordinal_position
|
71
|
-
SQL
|
23
|
+
VERSION_6_1_0 = Gem::Version.create "6.1.0"
|
24
|
+
VERSION_6_0_3 = Gem::Version.create "6.0.3"
|
72
25
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
26
|
+
def current_database
|
27
|
+
@connection.database_id
|
28
|
+
end
|
29
|
+
|
30
|
+
# Table
|
31
|
+
|
32
|
+
def data_sources
|
33
|
+
information_schema { |i| i.tables.map(&:name) }
|
34
|
+
end
|
35
|
+
alias tables data_sources
|
36
|
+
|
37
|
+
def table_exists? table_name
|
38
|
+
information_schema { |i| i.table table_name }.present?
|
39
|
+
end
|
40
|
+
alias data_source_exists? table_exists?
|
41
|
+
|
42
|
+
def create_table table_name, **options
|
43
|
+
td = create_table_definition table_name, options
|
44
|
+
|
45
|
+
if options[:id] != false
|
46
|
+
pk = options.fetch :primary_key do
|
47
|
+
Base.get_primary_key table_name.to_s.singularize
|
48
|
+
end
|
49
|
+
|
50
|
+
if pk.is_a? Array
|
51
|
+
td.primary_keys pk
|
52
|
+
else
|
53
|
+
td.primary_key pk, options.fetch(:id, :primary_key), **{}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
yield td if block_given?
|
58
|
+
|
59
|
+
statements = []
|
60
|
+
|
61
|
+
if options[:force]
|
62
|
+
statements.concat drop_table_with_indexes_sql(table_name, options)
|
63
|
+
end
|
64
|
+
|
65
|
+
statements << schema_creation.accept(td)
|
66
|
+
|
67
|
+
td.indexes.each do |column_name, index_options|
|
68
|
+
id = create_index_definition table_name, column_name, **index_options
|
69
|
+
statements << schema_creation.accept(id)
|
70
|
+
end
|
71
|
+
|
72
|
+
execute_schema_statements statements
|
73
|
+
end
|
74
|
+
|
75
|
+
def drop_table table_name, options = {}
|
76
|
+
statements = drop_table_with_indexes_sql table_name, options
|
77
|
+
execute_schema_statements statements
|
78
|
+
end
|
79
|
+
|
80
|
+
# Creates a join table that uses all the columns in the table as the primary key by default, unless
|
81
|
+
# an explicit primary key has been defined for the table. ActiveRecord will by default generate join
|
82
|
+
# tables without a primary key. Cloud Spanner however requires all tables to have a primary key.
|
83
|
+
# Instead of adding an additional column to the table only for the purpose of being the primary key,
|
84
|
+
# the Spanner ActiveRecord adapter defines a primary key that contains all the columns in the join
|
85
|
+
# table, as all values in the table should be unique anyways.
|
86
|
+
def create_join_table table_1, table_2, column_options: {}, **options
|
87
|
+
super do |td|
|
88
|
+
unless td.columns.any?(&:primary_key?)
|
89
|
+
td.columns.each do |col|
|
90
|
+
def col.primary_key?
|
91
|
+
true
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
yield td if block_given?
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def rename_table _table_name, _new_name
|
100
|
+
raise ActiveRecordSpannerAdapter::NotSupportedError, \
|
101
|
+
"rename_table is not implemented"
|
102
|
+
end
|
103
|
+
|
104
|
+
# Column
|
105
|
+
|
106
|
+
def column_definitions table_name
|
107
|
+
information_schema { |i| i.table_columns table_name }
|
108
|
+
end
|
109
|
+
|
110
|
+
def new_column_from_field _table_name, field
|
111
|
+
ConnectionAdapters::Column.new \
|
112
|
+
field.name,
|
113
|
+
field.default,
|
114
|
+
fetch_type_metadata(field.spanner_type, field.ordinal_position),
|
115
|
+
field.nullable
|
116
|
+
end
|
117
|
+
|
118
|
+
def fetch_type_metadata sql_type, ordinal_position = nil
|
119
|
+
Spanner::TypeMetadata.new \
|
120
|
+
super(sql_type), ordinal_position: ordinal_position
|
121
|
+
end
|
122
|
+
|
123
|
+
def add_column table_name, column_name, type, **options
|
124
|
+
# Add column with NOT NULL not supported by spanner.
|
125
|
+
# It is currently un-implemented state in spanner service.
|
126
|
+
nullable = options.delete(:null) == false
|
127
|
+
|
128
|
+
at = create_alter_table table_name
|
129
|
+
at.add_column column_name, type, **options
|
130
|
+
|
131
|
+
statements = [schema_creation.accept(at)]
|
132
|
+
|
133
|
+
# Alter NOT NULL
|
134
|
+
if nullable
|
135
|
+
cd = at.adds.first.column
|
136
|
+
cd.null = false
|
137
|
+
ccd = Spanner::ChangeColumnDefinition.new(
|
138
|
+
table_name, cd, column_name
|
82
139
|
)
|
140
|
+
statements << schema_creation.accept(ccd)
|
141
|
+
end
|
142
|
+
|
143
|
+
execute_schema_statements statements
|
144
|
+
end
|
145
|
+
|
146
|
+
def remove_column table_name, column_name, _type = nil, _options = {}
|
147
|
+
statements = drop_column_sql table_name, column_name
|
148
|
+
execute_schema_statements statements
|
149
|
+
end
|
150
|
+
|
151
|
+
if ActiveRecord.gem_version < VERSION_6_1_0
|
152
|
+
def remove_columns table_name, *column_names
|
153
|
+
_remove_columns table_name, *column_names
|
154
|
+
end
|
155
|
+
else
|
156
|
+
def remove_columns table_name, *column_names, _type: nil, **_options
|
157
|
+
_remove_columns table_name, *column_names
|
83
158
|
end
|
84
159
|
end
|
85
160
|
|
86
|
-
def
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
161
|
+
def _remove_columns table_name, *column_names
|
162
|
+
if column_names.empty?
|
163
|
+
raise ArgumentError, "You must specify at least one column name. "\
|
164
|
+
"Example: remove_columns(:people, :first_name)"
|
165
|
+
end
|
166
|
+
|
167
|
+
statements = []
|
168
|
+
|
169
|
+
column_names.each do |column_name|
|
170
|
+
statements.concat drop_column_sql(table_name, column_name)
|
171
|
+
end
|
172
|
+
|
173
|
+
execute_schema_statements statements
|
174
|
+
end
|
175
|
+
|
176
|
+
if ActiveRecord.gem_version < VERSION_6_1_0
|
177
|
+
def change_column table_name, column_name, type, options = {}
|
178
|
+
_change_column table_name, column_name, type, **options
|
179
|
+
end
|
180
|
+
else
|
181
|
+
def change_column table_name, column_name, type, **options
|
182
|
+
_change_column table_name, column_name, type, **options
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def change_column_null table_name, column_name, null, _default = nil
|
187
|
+
change_column table_name, column_name, nil, null: null
|
188
|
+
end
|
189
|
+
|
190
|
+
def change_column_default _table_name, _column_name, _default_or_changes
|
191
|
+
raise ActiveRecordSpannerAdapter::NotSupportedError, \
|
192
|
+
"change column with default value not supported."
|
193
|
+
end
|
194
|
+
|
195
|
+
def rename_column table_name, column_name, new_column_name
|
196
|
+
if ActiveRecord::Base.connection.ddl_batch?
|
197
|
+
raise ActiveRecordSpannerAdapter::NotSupportedError, \
|
198
|
+
"rename_column in a DDL Batch is not supported."
|
199
|
+
end
|
200
|
+
column = information_schema do |i|
|
201
|
+
i.table_column table_name, column_name
|
202
|
+
end
|
203
|
+
|
204
|
+
unless column
|
205
|
+
raise ArgumentError,
|
206
|
+
"Column '#{column_name}' not exist for table '#{table_name}'"
|
207
|
+
end
|
208
|
+
|
209
|
+
# Add Column
|
210
|
+
cast_type = lookup_cast_type column.spanner_type
|
211
|
+
add_column table_name, new_column_name, cast_type.type, **column.options
|
212
|
+
|
213
|
+
# Copy data
|
214
|
+
copy_data table_name, column_name, new_column_name
|
215
|
+
|
216
|
+
# Recreate Indexes
|
217
|
+
recreate_indexes table_name, column_name, new_column_name
|
218
|
+
|
219
|
+
# Recreate Foreign keys
|
220
|
+
recreate_foreign_keys table_name, column_name, new_column_name
|
221
|
+
|
222
|
+
# Drop Indexes, Drop Foreign keys and columns
|
223
|
+
remove_column table_name, column_name
|
224
|
+
end
|
225
|
+
|
226
|
+
# Index
|
227
|
+
|
228
|
+
def indexes table_name
|
229
|
+
result = information_schema do |i|
|
230
|
+
i.indexes table_name, index_type: "INDEX"
|
231
|
+
end
|
232
|
+
|
233
|
+
result.map do |index|
|
234
|
+
IndexDefinition.new(
|
235
|
+
index.table,
|
236
|
+
index.name,
|
237
|
+
index.columns.map(&:name),
|
238
|
+
unique: index.unique,
|
239
|
+
null_filtered: index.null_filtered,
|
240
|
+
interleave_in: index.interleave_in,
|
241
|
+
storing: index.storing,
|
242
|
+
orders: index.orders
|
111
243
|
)
|
112
244
|
end
|
113
245
|
end
|
114
246
|
|
115
|
-
def
|
116
|
-
|
117
|
-
index.type == 'PRIMARY_KEY'
|
118
|
-
}.columns
|
247
|
+
def index_name_exists? table_name, index_name
|
248
|
+
information_schema { |i| i.index table_name, index_name }.present?
|
119
249
|
end
|
120
250
|
|
121
|
-
def
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
251
|
+
def add_index table_name, column_name, options = {}
|
252
|
+
id = create_index_definition table_name, column_name, **options
|
253
|
+
|
254
|
+
if data_source_exists?(table_name) &&
|
255
|
+
index_name_exists?(table_name, id.name)
|
256
|
+
raise ArgumentError, "Index name '#{id.name}' on table" \
|
257
|
+
"'#{table_name}' already exists"
|
258
|
+
end
|
259
|
+
|
260
|
+
execute_schema_statements schema_creation.accept(id)
|
126
261
|
end
|
127
262
|
|
128
|
-
|
129
|
-
|
263
|
+
if ActiveRecord.gem_version < VERSION_6_1_0
|
264
|
+
def remove_index table_name, options = {}
|
265
|
+
index_name = index_name_for_remove table_name, options
|
266
|
+
execute "DROP INDEX #{quote_table_name index_name}"
|
267
|
+
end
|
268
|
+
else
|
269
|
+
def remove_index table_name, column_name = nil, **options
|
270
|
+
index_name = index_name_for_remove table_name, column_name, options
|
271
|
+
execute "DROP INDEX #{quote_table_name index_name}"
|
272
|
+
end
|
130
273
|
end
|
131
274
|
|
132
|
-
def
|
133
|
-
|
134
|
-
raise NotImplementedError, 'force in drop_table' if options[:force]
|
275
|
+
def rename_index table_name, old_name, new_name
|
276
|
+
validate_index_length! table_name, new_name
|
135
277
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
278
|
+
old_index = information_schema { |i| i.index table_name, old_name }
|
279
|
+
return unless old_index
|
280
|
+
|
281
|
+
statements = [
|
282
|
+
schema_creation.accept(DropIndexDefinition.new(old_name))
|
283
|
+
]
|
284
|
+
|
285
|
+
id = IndexDefinition.new \
|
286
|
+
old_index.table,
|
287
|
+
new_name,
|
288
|
+
old_index.columns.map(&:name),
|
289
|
+
unique: old_index.unique,
|
290
|
+
null_filtered: old_index.null_filtered,
|
291
|
+
interleave_in: old_index.interleave_in,
|
292
|
+
storing: old_index.storing,
|
293
|
+
orders: old_index.orders
|
294
|
+
|
295
|
+
statements << schema_creation.accept(id)
|
296
|
+
execute_schema_statements statements
|
297
|
+
end
|
298
|
+
|
299
|
+
# Primary Keys
|
300
|
+
|
301
|
+
def primary_keys table_name
|
302
|
+
columns = information_schema do |i|
|
303
|
+
i.table_primary_keys table_name
|
304
|
+
end
|
305
|
+
|
306
|
+
columns.map(&:name)
|
307
|
+
end
|
308
|
+
|
309
|
+
def primary_and_parent_keys table_name
|
310
|
+
columns = information_schema do |i|
|
311
|
+
i.table_primary_keys table_name, true
|
312
|
+
end
|
141
313
|
|
142
|
-
|
143
|
-
execute_ddl(*ddls)
|
314
|
+
columns.map(&:name)
|
144
315
|
end
|
145
316
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
317
|
+
# Foreign Keys
|
318
|
+
|
319
|
+
def foreign_keys table_name, column: nil
|
320
|
+
raise ArgumentError if table_name.blank?
|
321
|
+
|
322
|
+
result = information_schema { |i| i.foreign_keys table_name }
|
323
|
+
|
324
|
+
if column
|
325
|
+
result = result.select { |fk| fk.columns.include? column.to_s }
|
326
|
+
end
|
327
|
+
|
328
|
+
result.map do |fk|
|
329
|
+
options = {
|
330
|
+
column: fk.columns.first,
|
331
|
+
name: fk.name,
|
332
|
+
primary_key: fk.ref_columns.first,
|
333
|
+
on_delete: fk.on_update,
|
334
|
+
on_update: fk.on_update
|
335
|
+
}
|
336
|
+
|
337
|
+
ForeignKeyDefinition.new table_name, fk.ref_table, options
|
338
|
+
end
|
155
339
|
end
|
156
340
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
341
|
+
if ActiveRecord.gem_version < VERSION_6_0_3
|
342
|
+
def add_foreign_key from_table, to_table, options = {}
|
343
|
+
_add_foreign_key from_table, to_table, **options
|
344
|
+
end
|
345
|
+
else
|
346
|
+
def add_foreign_key from_table, to_table, **options
|
347
|
+
_add_foreign_key from_table, to_table, **options
|
162
348
|
end
|
163
349
|
end
|
164
350
|
|
351
|
+
def _add_foreign_key from_table, to_table, **options
|
352
|
+
options = foreign_key_options from_table, to_table, options
|
353
|
+
at = create_alter_table from_table
|
354
|
+
at.add_foreign_key to_table, options
|
355
|
+
|
356
|
+
execute_schema_statements schema_creation.accept(at)
|
357
|
+
end
|
358
|
+
|
359
|
+
def remove_foreign_key from_table, to_table = nil, **options
|
360
|
+
fk_name_to_delete = foreign_key_for!(
|
361
|
+
from_table, to_table: to_table, **options
|
362
|
+
).name
|
363
|
+
|
364
|
+
at = create_alter_table from_table
|
365
|
+
at.drop_foreign_key fk_name_to_delete
|
366
|
+
|
367
|
+
execute_schema_statements schema_creation.accept(at)
|
368
|
+
end
|
369
|
+
|
370
|
+
# Reference Column
|
371
|
+
|
372
|
+
def add_reference table_name, ref_name, **options
|
373
|
+
ReferenceDefinition.new(ref_name, **options).add_to(
|
374
|
+
update_table_definition(table_name, self)
|
375
|
+
)
|
376
|
+
end
|
377
|
+
alias add_belongs_to add_reference
|
378
|
+
|
379
|
+
def quoted_scope name = nil, type: nil
|
380
|
+
scope = { schema: quote("") }
|
381
|
+
scope[:name] = quote name if name
|
382
|
+
scope[:type] = quote type if type
|
383
|
+
scope
|
384
|
+
end
|
385
|
+
|
386
|
+
def create_schema_dumper options
|
387
|
+
SchemaDumper.create self, options
|
388
|
+
end
|
389
|
+
|
390
|
+
# rubocop:disable Lint/UnusedMethodArgument
|
391
|
+
def type_to_sql type, limit: nil, precision: nil, scale: nil, **opts
|
392
|
+
type = type.to_sym if type
|
393
|
+
native = native_database_types[type]
|
394
|
+
|
395
|
+
return type.to_s unless native
|
396
|
+
|
397
|
+
sql_type = (native.is_a?(Hash) ? native[:name] : native).dup
|
398
|
+
|
399
|
+
sql_type = "#{sql_type}(#{limit || native[:limit]})" if [:string, :text, :binary].include? type
|
400
|
+
sql_type = "ARRAY<#{sql_type}>" if opts[:array]
|
401
|
+
|
402
|
+
sql_type
|
403
|
+
end
|
404
|
+
# rubocop:enable Lint/UnusedMethodArgument
|
405
|
+
|
165
406
|
private
|
166
|
-
|
167
|
-
|
407
|
+
|
408
|
+
def schema_creation
|
409
|
+
SchemaCreation.new self
|
410
|
+
end
|
411
|
+
|
412
|
+
def create_table_definition *args
|
413
|
+
TableDefinition.new self, args[0], options: args[1]
|
414
|
+
end
|
415
|
+
|
416
|
+
def able_to_ddl_batch? table_name
|
417
|
+
[ActiveRecord::InternalMetadata.table_name, ActiveRecord::SchemaMigration.table_name].exclude? table_name.to_s
|
418
|
+
end
|
419
|
+
|
420
|
+
def _change_column table_name, column_name, type, **options # rubocop:disable Metrics/AbcSize
|
421
|
+
column = information_schema do |i|
|
422
|
+
i.table_column table_name, column_name
|
423
|
+
end
|
424
|
+
|
425
|
+
unless column
|
426
|
+
raise ArgumentError,
|
427
|
+
"Column '#{column_name}' not exist for table '#{table_name}'"
|
428
|
+
end
|
429
|
+
|
430
|
+
indexes = information_schema do |i|
|
431
|
+
i.indexes_by_columns table_name, column_name
|
432
|
+
end
|
433
|
+
|
434
|
+
statements = indexes.map do |index|
|
435
|
+
schema_creation.accept DropIndexDefinition.new(index.name)
|
436
|
+
end
|
437
|
+
|
438
|
+
column = new_column_from_field table_name, column
|
439
|
+
|
440
|
+
type ||= column.type
|
441
|
+
options[:null] = column.null unless options.key? :null
|
442
|
+
|
443
|
+
if ["STRING", "BYTES"].include? type
|
444
|
+
options[:limit] = column.limit unless options.key? :limit
|
445
|
+
end
|
446
|
+
|
447
|
+
# Only timestamp type can set commit timestamp
|
448
|
+
if type == "TIMESTAMP" && options.key?(:allow_commit_timestamp) == false
|
449
|
+
options[:allow_commit_timestamp] = column.allow_commit_timestamp
|
450
|
+
end
|
451
|
+
|
452
|
+
td = create_table_definition table_name
|
453
|
+
cd = td.new_column_definition column.name, type, **options
|
454
|
+
|
455
|
+
ccd = Spanner::ChangeColumnDefinition.new table_name, cd, column.name
|
456
|
+
statements << schema_creation.accept(ccd)
|
457
|
+
|
458
|
+
# Recreate indexes
|
459
|
+
indexes.each do |index|
|
460
|
+
id = create_index_definition(
|
461
|
+
table_name,
|
462
|
+
index.column_names,
|
463
|
+
**index.options
|
464
|
+
)
|
465
|
+
statements << schema_creation.accept(id)
|
466
|
+
end
|
467
|
+
|
468
|
+
execute_schema_statements statements
|
469
|
+
end
|
470
|
+
|
471
|
+
def copy_data table_name, src_column_name, dest_column_name
|
472
|
+
sql = "UPDATE %<table>s SET %<dest_column_name>s = %<src_column_name>s WHERE true"
|
473
|
+
values = {
|
474
|
+
table: table_name,
|
475
|
+
dest_column_name: quote_column_name(dest_column_name),
|
476
|
+
src_column_name: quote_column_name(src_column_name)
|
477
|
+
}
|
478
|
+
ActiveRecord::Base.connection.transaction isolation: :pdml do
|
479
|
+
execute sql % values
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
def recreate_indexes table_name, column_name, new_column_name
|
484
|
+
indexes = information_schema.indexes_by_columns table_name, column_name
|
485
|
+
indexes.each do |index|
|
486
|
+
remove_index table_name, name: index.name
|
487
|
+
options = index.rename_column_options column_name, new_column_name
|
488
|
+
options[:options][:name] = options[:options][:name].to_s.gsub(
|
489
|
+
column_name.to_s, new_column_name.to_s
|
490
|
+
)
|
491
|
+
add_index table_name, options[:columns], **options[:options]
|
492
|
+
end
|
493
|
+
end
|
494
|
+
|
495
|
+
def recreate_foreign_keys table_name, column_name, new_column_name
|
496
|
+
fkeys = foreign_keys table_name, column: column_name
|
497
|
+
fkeys.each do |fk|
|
498
|
+
remove_foreign_key table_name, name: fk.name
|
499
|
+
options = fk.options.except :column, :name
|
500
|
+
options[:column] = new_column_name
|
501
|
+
add_foreign_key table_name, fk.to_table, **options
|
502
|
+
end
|
503
|
+
end
|
504
|
+
|
505
|
+
def create_index_definition table_name, column_name, **options
|
506
|
+
column_names = index_column_names column_name
|
507
|
+
|
508
|
+
options.assert_valid_keys(
|
509
|
+
:unique, :order, :name, :where, :length, :internal, :using,
|
510
|
+
:algorithm, :type, :opclass, :interleave_in, :storing,
|
511
|
+
:null_filtered
|
512
|
+
)
|
513
|
+
|
514
|
+
index_name = options[:name].to_s if options.key? :name
|
515
|
+
index_name ||= index_name table_name, column_names
|
516
|
+
|
517
|
+
validate_index_length! table_name, index_name
|
518
|
+
|
519
|
+
IndexDefinition.new \
|
520
|
+
table_name,
|
521
|
+
index_name,
|
522
|
+
column_names,
|
523
|
+
unique: options[:unique],
|
524
|
+
null_filtered: options[:null_filtered],
|
525
|
+
interleave_in: options[:interleave_in],
|
526
|
+
storing: options[:storing],
|
527
|
+
orders: options[:order]
|
528
|
+
end
|
529
|
+
|
530
|
+
def drop_table_with_indexes_sql table_name, options
|
531
|
+
statements = []
|
532
|
+
|
533
|
+
table = information_schema { |i| i.table table_name, view: :indexes }
|
534
|
+
return statements unless table
|
535
|
+
|
536
|
+
table.indexes.each do |index|
|
537
|
+
next if index.primary?
|
538
|
+
|
539
|
+
statements << schema_creation.accept(
|
540
|
+
DropIndexDefinition.new(index.name)
|
541
|
+
)
|
542
|
+
end
|
543
|
+
|
544
|
+
statements << schema_creation.accept(
|
545
|
+
DropTableDefinition.new(table_name, options)
|
546
|
+
)
|
547
|
+
statements
|
548
|
+
end
|
549
|
+
|
550
|
+
def drop_column_sql table_name, column_name
|
551
|
+
indexes = information_schema do |i|
|
552
|
+
i.indexes_by_columns table_name, column_name
|
553
|
+
end
|
554
|
+
|
555
|
+
statements = indexes.map do |index|
|
556
|
+
schema_creation.accept DropIndexDefinition.new(index.name)
|
557
|
+
end
|
558
|
+
|
559
|
+
foreign_keys(table_name, column: column_name).each do |fk|
|
560
|
+
at = create_alter_table table_name
|
561
|
+
at.drop_foreign_key fk.name
|
562
|
+
statements << schema_creation.accept(at)
|
563
|
+
end
|
564
|
+
|
565
|
+
statements << schema_creation.accept(
|
566
|
+
DropColumnDefinition.new(table_name, column_name)
|
567
|
+
)
|
568
|
+
|
569
|
+
statements
|
570
|
+
end
|
571
|
+
|
572
|
+
def execute_schema_statements statements
|
573
|
+
execute_ddl statements
|
574
|
+
end
|
575
|
+
|
576
|
+
def information_schema
|
577
|
+
info_schema = \
|
578
|
+
ActiveRecordSpannerAdapter::Connection.information_schema @config
|
579
|
+
|
580
|
+
return info_schema unless block_given?
|
581
|
+
|
582
|
+
yield info_schema
|
168
583
|
end
|
169
584
|
end
|
170
585
|
end
|
171
586
|
end
|
172
587
|
end
|
173
|
-
|