activerecord-spanner-adapter 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +36 -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 +26 -0
- data/CODE_OF_CONDUCT.md +40 -0
- data/CONTRIBUTING.md +79 -0
- data/Gemfile +9 -4
- data/LICENSE +6 -6
- data/README.md +67 -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 +147 -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 +67 -0
- data/acceptance/cases/transactions/read_write_transactions_test.rb +248 -0
- data/acceptance/cases/type/all_types_test.rb +152 -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/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 +143 -0
- data/acceptance/test_helper.rb +260 -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/interleaved-tables/README.md +152 -0
- data/examples/snippets/interleaved-tables/Rakefile +13 -0
- data/examples/snippets/interleaved-tables/application.rb +109 -0
- data/examples/snippets/interleaved-tables/config/database.yml +8 -0
- data/examples/snippets/interleaved-tables/db/migrate/01_create_tables.rb +44 -0
- data/examples/snippets/interleaved-tables/db/schema.rb +32 -0
- data/examples/snippets/interleaved-tables/db/seeds.rb +40 -0
- data/examples/snippets/interleaved-tables/models/album.rb +15 -0
- data/examples/snippets/interleaved-tables/models/singer.rb +20 -0
- data/examples/snippets/interleaved-tables/models/track.rb +25 -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/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 +49 -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/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 +224 -269
- 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 +182 -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 +32 -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 +217 -0
- data/lib/activerecord_spanner_adapter/connection.rb +324 -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 +261 -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 +113 -0
- data/lib/activerecord_spanner_adapter/version.rb +9 -0
- data/lib/arel/visitors/spanner.rb +35 -0
- data/lib/spanner_client_ext.rb +82 -0
- data/renovate.json +5 -0
- metadata +387 -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
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Copyright 2020 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Use of this source code is governed by an MIT-style
|
|
4
|
+
# license that can be found in the LICENSE file or at
|
|
5
|
+
# https://opensource.org/licenses/MIT.
|
|
6
|
+
|
|
7
|
+
module ActiveRecordSpannerAdapter
|
|
8
|
+
class Error < StandardError
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class NotSupportedError < Error
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
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 ActiveRecordSpannerAdapter
|
|
8
|
+
class ForeignKey
|
|
9
|
+
attr_accessor :table_name, :name, :columns, :ref_table, :ref_columns,
|
|
10
|
+
:on_delete, :on_update
|
|
11
|
+
|
|
12
|
+
def initialize \
|
|
13
|
+
table_name,
|
|
14
|
+
name,
|
|
15
|
+
columns,
|
|
16
|
+
ref_table,
|
|
17
|
+
ref_columns,
|
|
18
|
+
on_delete: nil,
|
|
19
|
+
on_update: nil
|
|
20
|
+
@table_name = table_name
|
|
21
|
+
@name = name
|
|
22
|
+
@columns = Array(columns)
|
|
23
|
+
@ref_table = ref_table
|
|
24
|
+
@ref_columns = Array(ref_columns)
|
|
25
|
+
@on_delete = on_delete unless on_delete == "NO ACTION"
|
|
26
|
+
@on_update = on_update unless on_update == "NO ACTION"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
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 ActiveRecordSpannerAdapter
|
|
8
|
+
class Index
|
|
9
|
+
class Column
|
|
10
|
+
attr_accessor :table_name, :index_name, :name, :order, :ordinal_position
|
|
11
|
+
|
|
12
|
+
def initialize \
|
|
13
|
+
table_name,
|
|
14
|
+
index_name,
|
|
15
|
+
name,
|
|
16
|
+
order: nil,
|
|
17
|
+
ordinal_position: nil
|
|
18
|
+
@table_name = table_name.to_s
|
|
19
|
+
@index_name = index_name.to_s
|
|
20
|
+
@name = name.to_s
|
|
21
|
+
@order = order.to_s.upcase if order
|
|
22
|
+
@ordinal_position = ordinal_position
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def storing?
|
|
26
|
+
@ordinal_position.nil?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def desc?
|
|
30
|
+
@order == "DESC"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def desc!
|
|
34
|
+
@order = "DESC"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
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
|
+
require "activerecord_spanner_adapter/index/column"
|
|
8
|
+
|
|
9
|
+
module ActiveRecordSpannerAdapter
|
|
10
|
+
class Index
|
|
11
|
+
attr_accessor :table, :name, :columns, :type, :unique, :null_filtered,
|
|
12
|
+
:interleave_in, :storing, :state
|
|
13
|
+
|
|
14
|
+
def initialize \
|
|
15
|
+
table,
|
|
16
|
+
name,
|
|
17
|
+
columns,
|
|
18
|
+
type: nil,
|
|
19
|
+
unique: false,
|
|
20
|
+
null_filtered: false,
|
|
21
|
+
interleave_in: nil,
|
|
22
|
+
storing: nil,
|
|
23
|
+
state: nil
|
|
24
|
+
@table = table.to_s
|
|
25
|
+
@name = name.to_s
|
|
26
|
+
@columns = Array(columns)
|
|
27
|
+
@type = type
|
|
28
|
+
@unique = unique
|
|
29
|
+
@null_filtered = null_filtered
|
|
30
|
+
@interleave_in = interleave_in unless interleave_in.to_s.empty?
|
|
31
|
+
@storing = storing || []
|
|
32
|
+
@state = state
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def primary?
|
|
36
|
+
@type == "PRIMARY_KEY"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def columns_by_position
|
|
40
|
+
@columns.select(&:ordinal_position).sort do |c1, c2|
|
|
41
|
+
c1.ordinal_position <=> c2.ordinal_position
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def column_names
|
|
46
|
+
columns_by_position.map(&:name)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def orders
|
|
50
|
+
columns_by_position.each_with_object({}) do |c, r|
|
|
51
|
+
r[c.name] = c.desc? ? :desc : :asc
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def options
|
|
56
|
+
{
|
|
57
|
+
name: name,
|
|
58
|
+
order: orders,
|
|
59
|
+
unique: unique,
|
|
60
|
+
interleave_in: interleave_in,
|
|
61
|
+
null_filtered: null_filtered,
|
|
62
|
+
storing: storing
|
|
63
|
+
}.delete_if { |_, v| v.nil? }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def rename_column_options old_column, new_column
|
|
67
|
+
opts = options
|
|
68
|
+
|
|
69
|
+
opts[:order].transform_keys do |key|
|
|
70
|
+
key.to_s == new_column.to_s
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
columns = column_names.map do |c|
|
|
74
|
+
c.to_s == old_column.to_s ? new_column : c
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
{ options: opts, columns: columns }
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,261 @@
|
|
|
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
|
+
require "active_record/connection_adapters/abstract/quoting"
|
|
8
|
+
require "activerecord_spanner_adapter/information_schema"
|
|
9
|
+
require "activerecord_spanner_adapter/table"
|
|
10
|
+
require "activerecord_spanner_adapter/index"
|
|
11
|
+
require "activerecord_spanner_adapter/foreign_key"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
module ActiveRecordSpannerAdapter
|
|
15
|
+
class InformationSchema
|
|
16
|
+
include ActiveRecord::ConnectionAdapters::Quoting
|
|
17
|
+
|
|
18
|
+
attr_reader :connection
|
|
19
|
+
|
|
20
|
+
def initialize connection
|
|
21
|
+
@connection = connection
|
|
22
|
+
@mutex = Mutex.new
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def tables table_name: nil, schema_name: nil, view: nil
|
|
26
|
+
sql = +"SELECT TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, PARENT_TABLE_NAME, ON_DELETE_ACTION"
|
|
27
|
+
sql << " FROM INFORMATION_SCHEMA.TABLES"
|
|
28
|
+
sql << " WHERE TABLE_SCHEMA=%<schema_name>s"
|
|
29
|
+
sql << " AND TABLE_NAME=%<table_name>s" if table_name
|
|
30
|
+
|
|
31
|
+
rows = execute_query(
|
|
32
|
+
sql,
|
|
33
|
+
schema_name: (schema_name || ""), table_name: table_name
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
rows.map do |row|
|
|
37
|
+
table = Table.new(
|
|
38
|
+
row["TABLE_NAME"],
|
|
39
|
+
parent_table: row["PARENT_TABLE_NAME"],
|
|
40
|
+
on_delete: row["ON_DELETE_ACTION"],
|
|
41
|
+
schema_name: row["TABLE_SCHEMA"],
|
|
42
|
+
catalog: row["TABLE_CATALOG"]
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
if [:full, :columns].include? view
|
|
46
|
+
table.columns = table_columns table.name
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
if [:full, :indexes].include? view
|
|
50
|
+
table.indexes = indexes table.name
|
|
51
|
+
end
|
|
52
|
+
table
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def table table_name, schema_name: nil, view: nil
|
|
57
|
+
tables(
|
|
58
|
+
table_name: table_name,
|
|
59
|
+
schema_name: schema_name,
|
|
60
|
+
view: view
|
|
61
|
+
).first
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def table_columns table_name, column_name: nil
|
|
65
|
+
sql = +"SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, COLUMN_DEFAULT, ORDINAL_POSITION"
|
|
66
|
+
sql << " FROM INFORMATION_SCHEMA.COLUMNS"
|
|
67
|
+
sql << " WHERE TABLE_NAME=%<table_name>s"
|
|
68
|
+
sql << " AND COLUMN_NAME=%<column_name>s" if column_name
|
|
69
|
+
sql << " ORDER BY ORDINAL_POSITION ASC"
|
|
70
|
+
|
|
71
|
+
execute_query(
|
|
72
|
+
sql,
|
|
73
|
+
table_name: table_name,
|
|
74
|
+
column_name: column_name
|
|
75
|
+
).map do |row|
|
|
76
|
+
type, limit = parse_type_and_limit row["SPANNER_TYPE"]
|
|
77
|
+
Table::Column.new \
|
|
78
|
+
table_name,
|
|
79
|
+
row["COLUMN_NAME"],
|
|
80
|
+
type,
|
|
81
|
+
limit: limit,
|
|
82
|
+
ordinal_position: row["ORDINAL_POSITION"],
|
|
83
|
+
nullable: row["IS_NULLABLE"] == "YES",
|
|
84
|
+
default: row["COLUMN_DEFAULT"]
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def table_column table_name, column_name
|
|
89
|
+
table_columns(table_name, column_name: column_name).first
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Returns the primary key columns of the given table. By default it will only return the columns that are not part
|
|
93
|
+
# of the primary key of the parent table (if any). These are the columns that are considered the primary key by
|
|
94
|
+
# ActiveRecord. The parent primary key columns are filtered out by default to allow interleaved tables to be
|
|
95
|
+
# considered as tables with a single-column primary key by ActiveRecord. The actual primary key of the table will
|
|
96
|
+
# include both the parent primary key columns and the 'own' primary key columns of a table.
|
|
97
|
+
def table_primary_keys table_name, include_parent_keys = false
|
|
98
|
+
sql = +"WITH TABLE_PK_COLS AS ( "
|
|
99
|
+
sql << "SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION "
|
|
100
|
+
sql << "FROM INFORMATION_SCHEMA.INDEX_COLUMNS C "
|
|
101
|
+
sql << "WHERE C.INDEX_TYPE = 'PRIMARY_KEY' "
|
|
102
|
+
sql << "AND TABLE_CATALOG = '' "
|
|
103
|
+
sql << "AND TABLE_SCHEMA = '') "
|
|
104
|
+
sql << "SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION "
|
|
105
|
+
sql << "FROM TABLE_PK_COLS "
|
|
106
|
+
sql << "INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) "
|
|
107
|
+
sql << "WHERE TABLE_NAME = %<table_name>s "
|
|
108
|
+
sql << "AND TABLE_CATALOG = '' "
|
|
109
|
+
sql << "AND TABLE_SCHEMA = '' "
|
|
110
|
+
unless include_parent_keys
|
|
111
|
+
sql << "AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( "
|
|
112
|
+
sql << " SELECT COLUMN_NAME "
|
|
113
|
+
sql << " FROM TABLE_PK_COLS "
|
|
114
|
+
sql << " WHERE TABLE_NAME = T.PARENT_TABLE_NAME "
|
|
115
|
+
sql << ")) "
|
|
116
|
+
end
|
|
117
|
+
sql << "ORDER BY ORDINAL_POSITION"
|
|
118
|
+
execute_query(
|
|
119
|
+
sql,
|
|
120
|
+
table_name: table_name
|
|
121
|
+
).map do |row|
|
|
122
|
+
Index::Column.new \
|
|
123
|
+
table_name,
|
|
124
|
+
row["INDEX_NAME"],
|
|
125
|
+
row["COLUMN_NAME"],
|
|
126
|
+
order: row["COLUMN_ORDERING"],
|
|
127
|
+
ordinal_position: row["ORDINAL_POSITION"]
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def indexes table_name, index_name: nil, index_type: nil
|
|
132
|
+
table_indexes_columns = index_columns(
|
|
133
|
+
table_name,
|
|
134
|
+
index_name: index_name
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
sql = +"SELECT INDEX_NAME, INDEX_TYPE, IS_UNIQUE, IS_NULL_FILTERED, PARENT_TABLE_NAME, INDEX_STATE"
|
|
138
|
+
sql << " FROM INFORMATION_SCHEMA.INDEXES"
|
|
139
|
+
sql << " WHERE TABLE_NAME=%<table_name>s"
|
|
140
|
+
sql << " AND TABLE_CATALOG = ''"
|
|
141
|
+
sql << " AND TABLE_SCHEMA = ''"
|
|
142
|
+
sql << " AND INDEX_NAME=%<index_name>s" if index_name
|
|
143
|
+
sql << " AND INDEX_TYPE=%<index_type>s" if index_type
|
|
144
|
+
sql << " AND SPANNER_IS_MANAGED=FALSE"
|
|
145
|
+
|
|
146
|
+
execute_query(
|
|
147
|
+
sql,
|
|
148
|
+
table_name: table_name,
|
|
149
|
+
index_name: index_name,
|
|
150
|
+
index_type: index_type
|
|
151
|
+
).map do |row|
|
|
152
|
+
columns = []
|
|
153
|
+
storing = []
|
|
154
|
+
table_indexes_columns.each do |c|
|
|
155
|
+
next unless c.index_name == row["INDEX_NAME"]
|
|
156
|
+
if c.ordinal_position
|
|
157
|
+
columns << c
|
|
158
|
+
else
|
|
159
|
+
storing << c.name
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
Index.new \
|
|
164
|
+
table_name,
|
|
165
|
+
row["INDEX_NAME"],
|
|
166
|
+
columns,
|
|
167
|
+
type: row["INDEX_TYPE"],
|
|
168
|
+
unique: row["IS_UNIQUE"],
|
|
169
|
+
null_filtered: row["IS_NULL_FILTERED"],
|
|
170
|
+
interleave_in: row["PARENT_TABLE_NAME"],
|
|
171
|
+
storing: storing,
|
|
172
|
+
state: row["INDEX_STATE"]
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def index table_name, index_name
|
|
177
|
+
indexes(table_name, index_name: index_name).first
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def index_columns table_name, index_name: nil
|
|
181
|
+
sql = +"SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION"
|
|
182
|
+
sql << " FROM INFORMATION_SCHEMA.INDEX_COLUMNS"
|
|
183
|
+
sql << " WHERE TABLE_NAME=%<table_name>s"
|
|
184
|
+
sql << " AND TABLE_CATALOG = ''"
|
|
185
|
+
sql << " AND TABLE_SCHEMA = ''"
|
|
186
|
+
sql << " AND INDEX_NAME=%<index_name>s" if index_name
|
|
187
|
+
sql << " ORDER BY ORDINAL_POSITION ASC"
|
|
188
|
+
|
|
189
|
+
execute_query(
|
|
190
|
+
sql,
|
|
191
|
+
table_name: table_name, index_name: index_name
|
|
192
|
+
).map do |row|
|
|
193
|
+
Index::Column.new \
|
|
194
|
+
table_name,
|
|
195
|
+
row["INDEX_NAME"],
|
|
196
|
+
row["COLUMN_NAME"],
|
|
197
|
+
order: row["COLUMN_ORDERING"],
|
|
198
|
+
ordinal_position: row["ORDINAL_POSITION"]
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def indexes_by_columns table_name, column_names
|
|
203
|
+
column_names = Array(column_names).map(&:to_s)
|
|
204
|
+
|
|
205
|
+
indexes(table_name).select do |index|
|
|
206
|
+
index.columns.any? { |c| column_names.include? c.name }
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def foreign_keys table_name
|
|
211
|
+
sql = <<~SQL
|
|
212
|
+
SELECT cc.table_name AS to_table,
|
|
213
|
+
cc.column_name AS primary_key,
|
|
214
|
+
fk.column_name as column,
|
|
215
|
+
fk.constraint_name AS name,
|
|
216
|
+
rc.update_rule AS on_update,
|
|
217
|
+
rc.delete_rule AS on_delete
|
|
218
|
+
FROM information_schema.referential_constraints rc
|
|
219
|
+
INNER JOIN information_schema.key_column_usage fk ON rc.constraint_name = fk.constraint_name
|
|
220
|
+
INNER JOIN information_schema.constraint_column_usage cc ON rc.constraint_name = cc.constraint_name
|
|
221
|
+
WHERE fk.table_name = %<table_name>s
|
|
222
|
+
AND fk.constraint_schema = %<constraint_schema>s
|
|
223
|
+
SQL
|
|
224
|
+
|
|
225
|
+
rows = execute_query(
|
|
226
|
+
sql, table_name: table_name, constraint_schema: ""
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
rows.map do |row|
|
|
230
|
+
ForeignKey.new(
|
|
231
|
+
table_name,
|
|
232
|
+
row["name"],
|
|
233
|
+
row["column"],
|
|
234
|
+
row["to_table"],
|
|
235
|
+
row["primary_key"],
|
|
236
|
+
on_delete: row["on_delete"],
|
|
237
|
+
on_update: row["on_update"]
|
|
238
|
+
)
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def parse_type_and_limit value
|
|
243
|
+
matched = /^([A-Z]*)\((.*)\)/.match value
|
|
244
|
+
return [value] unless matched
|
|
245
|
+
|
|
246
|
+
limit = matched[2]
|
|
247
|
+
limit = limit.to_i unless limit == "MAX"
|
|
248
|
+
|
|
249
|
+
[matched[1], limit]
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
private
|
|
253
|
+
|
|
254
|
+
def execute_query sql, params = {}
|
|
255
|
+
params = params.transform_values { |v| quote v }
|
|
256
|
+
sql = format sql, params
|
|
257
|
+
|
|
258
|
+
@connection.execute_query(sql).rows
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
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 AttributeMethods
|
|
9
|
+
module PrimaryKey
|
|
10
|
+
module ClassMethods
|
|
11
|
+
def primary_and_parent_key
|
|
12
|
+
reset_primary_and_parent_key unless defined? @primary_and_parent_key
|
|
13
|
+
@primary_and_parent_key
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def reset_primary_and_parent_key
|
|
17
|
+
self.primary_and_parent_key = base_class? ? fetch_primary_and_parent_key : base_class.primary_and_parent_key
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def fetch_primary_and_parent_key
|
|
21
|
+
return connection.schema_cache.primary_and_parent_keys table_name \
|
|
22
|
+
if ActiveRecord::Base != self && table_exists?
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def primary_and_parent_key= value
|
|
26
|
+
@primary_and_parent_key = value
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|