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
|
@@ -0,0 +1,37 @@
|
|
|
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 TypeMetadata < DelegateClass(SqlTypeMetadata)
|
|
13
|
+
undef to_yaml if method_defined? :to_yaml
|
|
14
|
+
|
|
15
|
+
attr_reader :ordinal_position
|
|
16
|
+
|
|
17
|
+
def initialize type_metadata, ordinal_position: nil
|
|
18
|
+
super type_metadata
|
|
19
|
+
@ordinal_position = ordinal_position
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def == other
|
|
23
|
+
other.is_a?(TypeMetadata) &&
|
|
24
|
+
__getobj__ == other.__getobj__ &&
|
|
25
|
+
ordinal_position == other.ordinal_position
|
|
26
|
+
end
|
|
27
|
+
alias eql? ==
|
|
28
|
+
|
|
29
|
+
def hash
|
|
30
|
+
TypeMetadata.hash ^
|
|
31
|
+
__getobj__.hash ^
|
|
32
|
+
ordinal_position.hash
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -1,126 +1,233 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
require
|
|
8
|
-
require
|
|
9
|
-
require
|
|
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 "securerandom"
|
|
8
|
+
require "google/cloud/spanner"
|
|
9
|
+
require "spanner_client_ext"
|
|
10
|
+
require "active_record/connection_adapters/abstract_adapter"
|
|
11
|
+
require "active_record/connection_adapters/spanner/database_statements"
|
|
12
|
+
require "active_record/connection_adapters/spanner/schema_statements"
|
|
13
|
+
require "active_record/connection_adapters/spanner/schema_cache"
|
|
14
|
+
require "active_record/connection_adapters/spanner/schema_definitions"
|
|
15
|
+
require "active_record/connection_adapters/spanner/type_metadata"
|
|
16
|
+
require "active_record/connection_adapters/spanner/quoting"
|
|
17
|
+
require "active_record/type/spanner/array"
|
|
18
|
+
require "active_record/type/spanner/bytes"
|
|
19
|
+
require "active_record/type/spanner/spanner_active_record_converter"
|
|
20
|
+
require "active_record/type/spanner/time"
|
|
21
|
+
require "arel/visitors/spanner"
|
|
22
|
+
require "activerecord_spanner_adapter/base"
|
|
23
|
+
require "activerecord_spanner_adapter/connection"
|
|
24
|
+
require "activerecord_spanner_adapter/errors"
|
|
25
|
+
require "activerecord_spanner_adapter/information_schema"
|
|
26
|
+
require "activerecord_spanner_adapter/primary_key"
|
|
27
|
+
require "activerecord_spanner_adapter/transaction"
|
|
10
28
|
|
|
11
29
|
module ActiveRecord
|
|
12
|
-
module ConnectionHandling
|
|
13
|
-
def spanner_connection
|
|
14
|
-
|
|
30
|
+
module ConnectionHandling # :nodoc:
|
|
31
|
+
def spanner_connection config
|
|
32
|
+
connection = ActiveRecordSpannerAdapter::Connection.new config
|
|
33
|
+
connection.connect!
|
|
34
|
+
ConnectionAdapters::SpannerAdapter.new connection, logger, nil, config
|
|
35
|
+
rescue Google::Cloud::Error => error
|
|
36
|
+
if error.instance_of? Google::Cloud::NotFoundError
|
|
37
|
+
raise ActiveRecord::NoDatabaseError
|
|
38
|
+
end
|
|
39
|
+
raise error
|
|
15
40
|
end
|
|
16
41
|
end
|
|
17
42
|
|
|
18
43
|
module ConnectionAdapters
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
44
|
+
module AbstractPool
|
|
45
|
+
def get_schema_cache connection
|
|
46
|
+
@schema_cache ||= SpannerSchemaCache.new connection
|
|
47
|
+
@schema_cache.connection = connection
|
|
48
|
+
@schema_cache
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
23
52
|
class SpannerAdapter < AbstractAdapter
|
|
24
|
-
ADAPTER_NAME =
|
|
25
|
-
|
|
26
|
-
|
|
53
|
+
ADAPTER_NAME = "spanner".freeze
|
|
54
|
+
NATIVE_DATABASE_TYPES = {
|
|
55
|
+
primary_key: "INT64",
|
|
56
|
+
parent_key: "INT64",
|
|
57
|
+
string: { name: "STRING", limit: "MAX" },
|
|
58
|
+
text: { name: "STRING", limit: "MAX" },
|
|
59
|
+
integer: { name: "INT64" },
|
|
60
|
+
bigint: { name: "INT64" },
|
|
61
|
+
float: { name: "FLOAT64" },
|
|
62
|
+
decimal: { name: "NUMERIC" },
|
|
63
|
+
numeric: { name: "NUMERIC" },
|
|
64
|
+
datetime: { name: "TIMESTAMP" },
|
|
65
|
+
time: { name: "TIMESTAMP" },
|
|
66
|
+
date: { name: "DATE" },
|
|
67
|
+
binary: { name: "BYTES", limit: "MAX" },
|
|
68
|
+
boolean: { name: "BOOL" },
|
|
69
|
+
json: { name: "JSON" }
|
|
70
|
+
}.freeze
|
|
27
71
|
|
|
28
|
-
include Spanner::SchemaStatements
|
|
29
|
-
include Spanner::DatabaseStatements
|
|
30
72
|
include Spanner::Quoting
|
|
73
|
+
include Spanner::DatabaseStatements
|
|
74
|
+
include Spanner::SchemaStatements
|
|
31
75
|
|
|
32
|
-
def initialize
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
super(connection, logger, config)
|
|
76
|
+
def initialize connection, logger, connection_options, config
|
|
77
|
+
super connection, logger, config
|
|
78
|
+
@connection_options = connection_options
|
|
36
79
|
end
|
|
37
80
|
|
|
38
|
-
def
|
|
39
|
-
|
|
81
|
+
def max_identifier_length
|
|
82
|
+
128
|
|
40
83
|
end
|
|
41
84
|
|
|
42
|
-
def
|
|
43
|
-
|
|
85
|
+
def native_database_types
|
|
86
|
+
NATIVE_DATABASE_TYPES
|
|
44
87
|
end
|
|
45
88
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
89
|
+
# Database
|
|
90
|
+
|
|
91
|
+
def self.database_exists? config
|
|
92
|
+
connection = ActiveRecordSpannerAdapter::Connection.new config
|
|
93
|
+
connection.connect!
|
|
94
|
+
true
|
|
95
|
+
rescue ActiveRecord::NoDatabaseError
|
|
96
|
+
false
|
|
51
97
|
end
|
|
52
98
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
@
|
|
57
|
-
@database_id = params[:database]
|
|
99
|
+
# Connection management
|
|
100
|
+
|
|
101
|
+
def active?
|
|
102
|
+
@connection.active?
|
|
58
103
|
end
|
|
59
104
|
|
|
60
105
|
def disconnect!
|
|
61
106
|
super
|
|
62
|
-
|
|
107
|
+
@connection.disconnect!
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def reset!
|
|
111
|
+
super
|
|
112
|
+
@connection.reset!
|
|
113
|
+
end
|
|
114
|
+
alias reconnect! reset!
|
|
115
|
+
|
|
116
|
+
# Spanner Connection API
|
|
117
|
+
delegate :ddl_batch, :ddl_batch?, :start_batch_ddl, :abort_batch, :run_batch, to: :@connection
|
|
118
|
+
|
|
119
|
+
def current_spanner_transaction
|
|
120
|
+
@connection.current_transaction
|
|
63
121
|
end
|
|
64
122
|
|
|
65
|
-
|
|
123
|
+
# Supported features
|
|
124
|
+
|
|
125
|
+
def supports_bulk_alter?
|
|
66
126
|
true
|
|
67
127
|
end
|
|
68
128
|
|
|
69
|
-
def
|
|
70
|
-
|
|
71
|
-
SecureRandom.uuid
|
|
129
|
+
def supports_common_table_expressions?
|
|
130
|
+
true
|
|
72
131
|
end
|
|
73
132
|
|
|
74
|
-
|
|
75
|
-
|
|
133
|
+
def supports_explain?
|
|
134
|
+
false
|
|
135
|
+
end
|
|
76
136
|
|
|
77
|
-
def
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
137
|
+
def supports_foreign_keys?
|
|
138
|
+
true
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def supports_index_sort_order?
|
|
142
|
+
true
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def supports_insert_on_conflict?
|
|
146
|
+
true
|
|
147
|
+
end
|
|
148
|
+
alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
|
|
149
|
+
alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
|
|
150
|
+
alias supports_insert_conflict_target? supports_insert_on_conflict?
|
|
151
|
+
|
|
152
|
+
def supports_insert_returning?
|
|
153
|
+
true
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def supports_multi_insert?
|
|
157
|
+
true
|
|
88
158
|
end
|
|
89
159
|
|
|
90
|
-
def
|
|
91
|
-
|
|
92
|
-
|
|
160
|
+
def supports_optimizer_hints?
|
|
161
|
+
true
|
|
162
|
+
end
|
|
93
163
|
|
|
94
|
-
|
|
164
|
+
def supports_primary_key?
|
|
165
|
+
true
|
|
95
166
|
end
|
|
96
167
|
|
|
97
|
-
def
|
|
98
|
-
|
|
168
|
+
def prefetch_primary_key? _table_name = nil
|
|
169
|
+
true
|
|
170
|
+
end
|
|
99
171
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
172
|
+
# Generate next sequence number for primary key
|
|
173
|
+
def next_sequence_value _sequence_name
|
|
174
|
+
SecureRandom.uuid.gsub("-", "").hex & 0x7FFFFFFFFFFFFFFF
|
|
175
|
+
end
|
|
104
176
|
|
|
105
|
-
|
|
177
|
+
def arel_visitor
|
|
178
|
+
Arel::Visitors::Spanner.new self
|
|
106
179
|
end
|
|
107
180
|
|
|
108
|
-
|
|
109
|
-
|
|
181
|
+
private
|
|
182
|
+
|
|
183
|
+
def initialize_type_map m = type_map
|
|
184
|
+
m.register_type "BOOL", Type::Boolean.new
|
|
185
|
+
register_class_with_limit(
|
|
186
|
+
m, %r{^BYTES}i, ActiveRecord::Type::Spanner::Bytes
|
|
187
|
+
)
|
|
188
|
+
m.register_type "DATE", Type::Date.new
|
|
189
|
+
m.register_type "FLOAT64", Type::Float.new
|
|
190
|
+
m.register_type "NUMERIC", Type::Decimal.new
|
|
191
|
+
m.register_type "INT64", Type::Integer.new(limit: 8)
|
|
192
|
+
register_class_with_limit m, %r{^STRING}i, Type::String
|
|
193
|
+
m.register_type "TIMESTAMP", ActiveRecord::Type::Spanner::Time.new
|
|
194
|
+
m.register_type "JSON", ActiveRecord::Type::Json.new
|
|
195
|
+
|
|
196
|
+
register_array_types m
|
|
110
197
|
end
|
|
111
198
|
|
|
112
|
-
def
|
|
113
|
-
|
|
199
|
+
def register_array_types m
|
|
200
|
+
m.register_type %r{^ARRAY<BOOL>}i, Type::Spanner::Array.new(Type::Boolean.new)
|
|
201
|
+
m.register_type %r{^ARRAY<BYTES\((MAX|d+)\)>}i, Type::Spanner::Array.new(Type::Binary.new)
|
|
202
|
+
m.register_type %r{^ARRAY<DATE>}i, Type::Spanner::Array.new(Type::Date.new)
|
|
203
|
+
m.register_type %r{^ARRAY<FLOAT64>}i, Type::Spanner::Array.new(Type::Float.new)
|
|
204
|
+
m.register_type %r{^ARRAY<NUMERIC>}i, Type::Spanner::Array.new(Type::Decimal.new)
|
|
205
|
+
m.register_type %r{^ARRAY<INT64>}i, Type::Spanner::Array.new(Type::Integer.new(limit: 8))
|
|
206
|
+
m.register_type %r{^ARRAY<STRING\((MAX|d+)\)>}i, Type::Spanner::Array.new(Type::String.new)
|
|
207
|
+
m.register_type %r{^ARRAY<TIMESTAMP>}i, Type::Spanner::Array.new(ActiveRecord::Type::Spanner::Time.new)
|
|
208
|
+
m.register_type %r{^ARRAY<JSON>}i, Type::Spanner::Array.new(ActiveRecord::Type::Json.new)
|
|
114
209
|
end
|
|
115
210
|
|
|
116
|
-
def
|
|
117
|
-
|
|
118
|
-
|
|
211
|
+
def extract_limit sql_type
|
|
212
|
+
value = /\((.*)\)/.match sql_type
|
|
213
|
+
return unless value
|
|
214
|
+
|
|
215
|
+
value[1] == "MAX" ? "MAX" : value[1].to_i
|
|
119
216
|
end
|
|
120
217
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
218
|
+
def translate_exception exception, message:, sql:, binds:
|
|
219
|
+
if exception.is_a? Google::Cloud::FailedPreconditionError
|
|
220
|
+
case exception.message
|
|
221
|
+
when /.*does not specify a non-null value for these NOT NULL columns.*/,
|
|
222
|
+
/.*must not be NULL.*/
|
|
223
|
+
NotNullViolation.new message, sql: sql, binds: binds
|
|
224
|
+
else
|
|
225
|
+
super
|
|
226
|
+
end
|
|
227
|
+
else
|
|
228
|
+
super
|
|
229
|
+
end
|
|
230
|
+
end
|
|
124
231
|
end
|
|
125
232
|
end
|
|
126
233
|
end
|
|
@@ -0,0 +1,74 @@
|
|
|
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/spanner_adapter"
|
|
8
|
+
|
|
9
|
+
module ActiveRecord
|
|
10
|
+
module Tasks
|
|
11
|
+
class SpannerDatabaseTasks
|
|
12
|
+
def initialize config
|
|
13
|
+
config = config.symbolize_keys
|
|
14
|
+
@connection = ActiveRecordSpannerAdapter::Connection.new config
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def create
|
|
18
|
+
@connection.create_database
|
|
19
|
+
rescue Google::Cloud::Error => error
|
|
20
|
+
if error.instance_of? Google::Cloud::AlreadyExistsError
|
|
21
|
+
raise ActiveRecord::DatabaseAlreadyExists
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
raise error
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def drop
|
|
28
|
+
@connection.database.drop
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def purge
|
|
32
|
+
drop
|
|
33
|
+
create
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def charset
|
|
37
|
+
nil
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def collation
|
|
41
|
+
nil
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def structure_dump filename, _extra_flags
|
|
45
|
+
file = File.open filename, "w"
|
|
46
|
+
ignore_tables = ActiveRecord::SchemaDumper.ignore_tables
|
|
47
|
+
|
|
48
|
+
if ignore_tables.any?
|
|
49
|
+
index_regx = /^CREATE(.*)INDEX(.*)ON (#{ignore_tables.join "|"})\(/
|
|
50
|
+
table_regx = /^CREATE TABLE (#{ignore_tables.join "|"})/
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
@connection.database.ddl(force: true).each do |statement|
|
|
54
|
+
next if ignore_tables.any? &&
|
|
55
|
+
(table_regx =~ statement || index_regx =~ statement)
|
|
56
|
+
file.write statement
|
|
57
|
+
file.write "\n"
|
|
58
|
+
end
|
|
59
|
+
ensure
|
|
60
|
+
file.close
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def structure_load filename, _extra_flags
|
|
64
|
+
statements = File.read(filename).split(/(?=^CREATE)/)
|
|
65
|
+
@connection.execute_ddl statements
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
DatabaseTasks.register_task(
|
|
70
|
+
/spanner/,
|
|
71
|
+
"ActiveRecord::Tasks::SpannerDatabaseTasks"
|
|
72
|
+
)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Copyright 2021 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Use of this source code is governed by an MIT-style
|
|
4
|
+
# license that can be found in the LICENSE file or at
|
|
5
|
+
# https://opensource.org/licenses/MIT.
|
|
6
|
+
|
|
7
|
+
module ActiveRecord
|
|
8
|
+
module Type
|
|
9
|
+
module Spanner
|
|
10
|
+
class Array < Type::Value
|
|
11
|
+
attr_reader :element_type
|
|
12
|
+
delegate :type, :user_input_in_time_zone, :limit, :precision, :scale, to: :element_type
|
|
13
|
+
|
|
14
|
+
def initialize element_type
|
|
15
|
+
@element_type = element_type
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def serialize value
|
|
19
|
+
return super if value.nil?
|
|
20
|
+
return super unless @element_type.is_a? Type::Decimal
|
|
21
|
+
return super unless value.respond_to? :map
|
|
22
|
+
|
|
23
|
+
# Convert a decimal (NUMERIC) array to a String array to prevent it from being encoded as a FLOAT64 array.
|
|
24
|
+
value.map do |v|
|
|
25
|
+
next if v.nil?
|
|
26
|
+
v.to_s
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
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 Type
|
|
11
|
+
module Spanner
|
|
12
|
+
class Bytes < ActiveRecord::Type::Binary
|
|
13
|
+
def serialize value
|
|
14
|
+
return super value if value.nil?
|
|
15
|
+
|
|
16
|
+
if value.respond_to?(:read) && value.respond_to?(:rewind)
|
|
17
|
+
value.rewind
|
|
18
|
+
value = value.read
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
Base64.strict_encode64 value.force_encoding("ASCII-8BIT")
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Copyright 2021 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Use of this source code is governed by an MIT-style
|
|
4
|
+
# license that can be found in the LICENSE file or at
|
|
5
|
+
# https://opensource.org/licenses/MIT.
|
|
6
|
+
|
|
7
|
+
# frozen_string_literal: true
|
|
8
|
+
|
|
9
|
+
module ActiveRecord
|
|
10
|
+
module Type
|
|
11
|
+
module Spanner
|
|
12
|
+
class SpannerActiveRecordConverter
|
|
13
|
+
##
|
|
14
|
+
# Converts an ActiveModel::Type to a Spanner type code.
|
|
15
|
+
def self.convert_active_model_type_to_spanner type # rubocop:disable Metrics/CyclomaticComplexity
|
|
16
|
+
case type
|
|
17
|
+
when NilClass then nil
|
|
18
|
+
when ActiveModel::Type::Integer, ActiveModel::Type::BigInteger then :INT64
|
|
19
|
+
when ActiveModel::Type::Boolean then :BOOL
|
|
20
|
+
when ActiveModel::Type::String, ActiveModel::Type::ImmutableString then :STRING
|
|
21
|
+
when ActiveModel::Type::Binary, ActiveRecord::Type::Spanner::Bytes then :BYTES
|
|
22
|
+
when ActiveModel::Type::Float then :FLOAT64
|
|
23
|
+
when ActiveModel::Type::Decimal then :NUMERIC
|
|
24
|
+
when ActiveModel::Type::DateTime, ActiveModel::Type::Time, ActiveRecord::Type::Spanner::Time then :TIMESTAMP
|
|
25
|
+
when ActiveModel::Type::Date then :DATE
|
|
26
|
+
when ActiveRecord::Type::Json then :JSON
|
|
27
|
+
when ActiveRecord::Type::Spanner::Array then [convert_active_model_type_to_spanner(type.element_type)]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
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 Type
|
|
11
|
+
module Spanner
|
|
12
|
+
class Time < ActiveRecord::Type::Time
|
|
13
|
+
def serialize value, *options
|
|
14
|
+
return "PENDING_COMMIT_TIMESTAMP()" if value == :commit_timestamp && options.length && options[0] == :dml
|
|
15
|
+
return "spanner.commit_timestamp()" if value == :commit_timestamp && options.length && options[0] == :mutation
|
|
16
|
+
val = super value
|
|
17
|
+
val.acts_like?(:time) ? val.utc.rfc3339(9) : val
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def user_input_in_time_zone value
|
|
21
|
+
return value.in_time_zone if value.is_a? ::Time
|
|
22
|
+
super value
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def cast_value value
|
|
28
|
+
if value.is_a? ::String
|
|
29
|
+
value = value.empty? ? nil : ::Time.parse(value)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
value
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
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/version"
|
|
8
|
+
|
|
9
|
+
if defined?(Rails)
|
|
10
|
+
module ActiveRecord
|
|
11
|
+
module ConnectionAdapters
|
|
12
|
+
class SpannerRailtie < ::Rails::Railtie
|
|
13
|
+
rake_tasks do
|
|
14
|
+
require "active_record/tasks/spanner_database_tasks"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
ActiveSupport.on_load :active_record do
|
|
18
|
+
require "active_record/connection_adapters/spanner_adapter"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|