activerecord-spanner-adapter 1.2.1 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/CODEOWNERS +1 -1
- data/.github/blunderbuss.yml +1 -1
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +29 -0
- data/README.md +7 -2
- data/acceptance/cases/migration/schema_dumper_test.rb +69 -0
- data/acceptance/cases/models/generated_column_test.rb +21 -7
- data/acceptance/cases/models/interleave_test.rb +36 -0
- data/acceptance/cases/models/logging_test.rb +53 -0
- data/acceptance/cases/models/query_test.rb +6 -1
- data/acceptance/cases/tasks/database_tasks_test.rb +407 -0
- data/acceptance/models/album_partial_disabled.rb +17 -0
- data/acceptance/schema/schema.rb +139 -134
- data/acceptance/test_helper.rb +2 -0
- data/examples/snippets/array-data-type/db/schema.rb +8 -3
- data/examples/snippets/bulk-insert/db/schema.rb +9 -4
- data/examples/snippets/commit-timestamp/db/schema.rb +11 -6
- data/examples/snippets/create-records/db/schema.rb +9 -4
- data/examples/snippets/date-data-type/db/schema.rb +8 -3
- data/examples/snippets/generated-column/db/schema.rb +6 -1
- data/examples/snippets/hints/db/schema.rb +6 -1
- data/examples/snippets/interleaved-tables/README.md +2 -2
- data/examples/snippets/interleaved-tables/db/schema.rb +5 -0
- data/examples/snippets/migrations/db/schema.rb +10 -5
- data/examples/snippets/mutations/db/schema.rb +9 -4
- data/examples/snippets/optimistic-locking/db/schema.rb +9 -4
- data/examples/snippets/partitioned-dml/db/schema.rb +5 -0
- data/examples/snippets/quickstart/db/schema.rb +9 -4
- data/examples/snippets/read-only-transactions/db/schema.rb +5 -0
- data/examples/snippets/read-write-transactions/db/schema.rb +9 -4
- data/examples/snippets/stale-reads/db/schema.rb +5 -0
- data/examples/snippets/timestamp-data-type/db/schema.rb +8 -3
- data/lib/active_record/connection_adapters/spanner/column.rb +23 -0
- data/lib/active_record/connection_adapters/spanner/database_statements.rb +7 -4
- data/lib/active_record/connection_adapters/spanner/quoting.rb +9 -0
- data/lib/active_record/connection_adapters/spanner/schema_creation.rb +17 -4
- data/lib/active_record/connection_adapters/spanner/schema_definitions.rb +11 -2
- data/lib/active_record/connection_adapters/spanner/schema_dumper.rb +56 -0
- data/lib/active_record/connection_adapters/spanner/schema_statements.rb +56 -9
- data/lib/active_record/connection_adapters/spanner/type_metadata.rb +19 -4
- data/lib/active_record/connection_adapters/spanner_adapter.rb +11 -0
- data/lib/active_record/tasks/spanner_database_tasks.rb +18 -4
- data/lib/active_record/type/spanner/spanner_active_record_converter.rb +10 -0
- data/lib/active_record/type/spanner/time.rb +10 -3
- data/lib/activerecord_spanner_adapter/base.rb +41 -27
- data/lib/activerecord_spanner_adapter/connection.rb +8 -3
- data/lib/activerecord_spanner_adapter/information_schema.rb +52 -3
- data/lib/activerecord_spanner_adapter/table/column.rb +7 -2
- data/lib/activerecord_spanner_adapter/version.rb +1 -1
- data/lib/arel/visitors/spanner.rb +8 -2
- metadata +8 -2
@@ -2,8 +2,8 @@
|
|
2
2
|
# of editing this file, please use the migrations feature of Active Record to
|
3
3
|
# incrementally modify your database, and then regenerate this schema definition.
|
4
4
|
#
|
5
|
-
# This file is the source Rails uses to define your schema when running `rails
|
6
|
-
# db:schema:load`. When creating a new database, `rails db:schema:load` tends to
|
5
|
+
# This file is the source Rails uses to define your schema when running `bin/rails
|
6
|
+
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
|
7
7
|
# be faster and is potentially less error prone than running all of your
|
8
8
|
# migrations from scratch. Old migrations may fail to apply correctly if those
|
9
9
|
# migrations use external dependencies or application code.
|
@@ -11,19 +11,24 @@
|
|
11
11
|
# It's strongly recommended that you check this file into your version control system.
|
12
12
|
|
13
13
|
ActiveRecord::Schema.define(version: 1) do
|
14
|
+
connection.start_batch_ddl
|
14
15
|
|
15
|
-
create_table "albums", force: :cascade do |t|
|
16
|
+
create_table "albums", id: { limit: 8 }, force: :cascade do |t|
|
16
17
|
t.string "title"
|
17
18
|
t.decimal "marketing_budget"
|
18
19
|
t.integer "singer_id", limit: 8
|
19
20
|
t.integer "lock_version", limit: 8
|
20
21
|
end
|
21
22
|
|
22
|
-
create_table "singers", force: :cascade do |t|
|
23
|
+
create_table "singers", id: { limit: 8 }, force: :cascade do |t|
|
23
24
|
t.string "first_name"
|
24
25
|
t.string "last_name"
|
25
26
|
t.integer "lock_version", limit: 8
|
26
27
|
end
|
27
28
|
|
28
29
|
add_foreign_key "albums", "singers"
|
30
|
+
connection.run_batch
|
31
|
+
rescue
|
32
|
+
abort_batch
|
33
|
+
raise
|
29
34
|
end
|
@@ -11,6 +11,7 @@
|
|
11
11
|
# It's strongly recommended that you check this file into your version control system.
|
12
12
|
|
13
13
|
ActiveRecord::Schema.define(version: 1) do
|
14
|
+
connection.start_batch_ddl
|
14
15
|
|
15
16
|
create_table "albums", id: { limit: 8 }, force: :cascade do |t|
|
16
17
|
t.string "title"
|
@@ -23,4 +24,8 @@ ActiveRecord::Schema.define(version: 1) do
|
|
23
24
|
end
|
24
25
|
|
25
26
|
add_foreign_key "albums", "singers"
|
27
|
+
connection.run_batch
|
28
|
+
rescue
|
29
|
+
abort_batch
|
30
|
+
raise
|
26
31
|
end
|
@@ -2,8 +2,8 @@
|
|
2
2
|
# of editing this file, please use the migrations feature of Active Record to
|
3
3
|
# incrementally modify your database, and then regenerate this schema definition.
|
4
4
|
#
|
5
|
-
# This file is the source Rails uses to define your schema when running `rails
|
6
|
-
# db:schema:load`. When creating a new database, `rails db:schema:load` tends to
|
5
|
+
# This file is the source Rails uses to define your schema when running `bin/rails
|
6
|
+
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
|
7
7
|
# be faster and is potentially less error prone than running all of your
|
8
8
|
# migrations from scratch. Old migrations may fail to apply correctly if those
|
9
9
|
# migrations use external dependencies or application code.
|
@@ -11,16 +11,21 @@
|
|
11
11
|
# It's strongly recommended that you check this file into your version control system.
|
12
12
|
|
13
13
|
ActiveRecord::Schema.define(version: 1) do
|
14
|
+
connection.start_batch_ddl
|
14
15
|
|
15
|
-
create_table "albums", force: :cascade do |t|
|
16
|
+
create_table "albums", id: { limit: 8 }, force: :cascade do |t|
|
16
17
|
t.string "title"
|
17
18
|
t.integer "singer_id", limit: 8
|
18
19
|
end
|
19
20
|
|
20
|
-
create_table "singers", force: :cascade do |t|
|
21
|
+
create_table "singers", id: { limit: 8 }, force: :cascade do |t|
|
21
22
|
t.string "first_name"
|
22
23
|
t.string "last_name"
|
23
24
|
end
|
24
25
|
|
25
26
|
add_foreign_key "albums", "singers"
|
27
|
+
connection.run_batch
|
28
|
+
rescue
|
29
|
+
abort_batch
|
30
|
+
raise
|
26
31
|
end
|
@@ -11,6 +11,7 @@
|
|
11
11
|
# It's strongly recommended that you check this file into your version control system.
|
12
12
|
|
13
13
|
ActiveRecord::Schema.define(version: 1) do
|
14
|
+
connection.start_batch_ddl
|
14
15
|
|
15
16
|
create_table "albums", id: { limit: 8 }, force: :cascade do |t|
|
16
17
|
t.string "title"
|
@@ -23,4 +24,8 @@ ActiveRecord::Schema.define(version: 1) do
|
|
23
24
|
end
|
24
25
|
|
25
26
|
add_foreign_key "albums", "singers"
|
27
|
+
connection.run_batch
|
28
|
+
rescue
|
29
|
+
abort_batch
|
30
|
+
raise
|
26
31
|
end
|
@@ -2,8 +2,8 @@
|
|
2
2
|
# of editing this file, please use the migrations feature of Active Record to
|
3
3
|
# incrementally modify your database, and then regenerate this schema definition.
|
4
4
|
#
|
5
|
-
# This file is the source Rails uses to define your schema when running `rails
|
6
|
-
# db:schema:load`. When creating a new database, `rails db:schema:load` tends to
|
5
|
+
# This file is the source Rails uses to define your schema when running `bin/rails
|
6
|
+
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
|
7
7
|
# be faster and is potentially less error prone than running all of your
|
8
8
|
# migrations from scratch. Old migrations may fail to apply correctly if those
|
9
9
|
# migrations use external dependencies or application code.
|
@@ -11,17 +11,22 @@
|
|
11
11
|
# It's strongly recommended that you check this file into your version control system.
|
12
12
|
|
13
13
|
ActiveRecord::Schema.define(version: 1) do
|
14
|
+
connection.start_batch_ddl
|
14
15
|
|
15
|
-
create_table "albums", force: :cascade do |t|
|
16
|
+
create_table "albums", id: { limit: 8 }, force: :cascade do |t|
|
16
17
|
t.string "title"
|
17
18
|
t.decimal "marketing_budget"
|
18
19
|
t.integer "singer_id", limit: 8
|
19
20
|
end
|
20
21
|
|
21
|
-
create_table "singers", force: :cascade do |t|
|
22
|
+
create_table "singers", id: { limit: 8 }, force: :cascade do |t|
|
22
23
|
t.string "first_name"
|
23
24
|
t.string "last_name"
|
24
25
|
end
|
25
26
|
|
26
27
|
add_foreign_key "albums", "singers"
|
28
|
+
connection.run_batch
|
29
|
+
rescue
|
30
|
+
abort_batch
|
31
|
+
raise
|
27
32
|
end
|
@@ -11,6 +11,7 @@
|
|
11
11
|
# It's strongly recommended that you check this file into your version control system.
|
12
12
|
|
13
13
|
ActiveRecord::Schema.define(version: 1) do
|
14
|
+
connection.start_batch_ddl
|
14
15
|
|
15
16
|
create_table "albums", id: { limit: 8 }, force: :cascade do |t|
|
16
17
|
t.string "title"
|
@@ -23,4 +24,8 @@ ActiveRecord::Schema.define(version: 1) do
|
|
23
24
|
end
|
24
25
|
|
25
26
|
add_foreign_key "albums", "singers"
|
27
|
+
connection.run_batch
|
28
|
+
rescue
|
29
|
+
abort_batch
|
30
|
+
raise
|
26
31
|
end
|
@@ -2,8 +2,8 @@
|
|
2
2
|
# of editing this file, please use the migrations feature of Active Record to
|
3
3
|
# incrementally modify your database, and then regenerate this schema definition.
|
4
4
|
#
|
5
|
-
# This file is the source Rails uses to define your schema when running `rails
|
6
|
-
# db:schema:load`. When creating a new database, `rails db:schema:load` tends to
|
5
|
+
# This file is the source Rails uses to define your schema when running `bin/rails
|
6
|
+
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
|
7
7
|
# be faster and is potentially less error prone than running all of your
|
8
8
|
# migrations from scratch. Old migrations may fail to apply correctly if those
|
9
9
|
# migrations use external dependencies or application code.
|
@@ -11,11 +11,16 @@
|
|
11
11
|
# It's strongly recommended that you check this file into your version control system.
|
12
12
|
|
13
13
|
ActiveRecord::Schema.define(version: 1) do
|
14
|
+
connection.start_batch_ddl
|
14
15
|
|
15
|
-
create_table "meetings", force: :cascade do |t|
|
16
|
+
create_table "meetings", id: { limit: 8 }, force: :cascade do |t|
|
16
17
|
t.string "title"
|
17
18
|
t.time "meeting_time"
|
18
19
|
t.string "meeting_timezone"
|
19
20
|
end
|
20
21
|
|
22
|
+
connection.run_batch
|
23
|
+
rescue
|
24
|
+
abort_batch
|
25
|
+
raise
|
21
26
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# Copyright 2022 Google LLC
|
2
|
+
#
|
3
|
+
# Use of this source code is governed by an MIT-style
|
4
|
+
# license that can be found in the LICENSE file or at
|
5
|
+
# https://opensource.org/licenses/MIT.
|
6
|
+
#
|
7
|
+
# frozen_string_literal: true
|
8
|
+
|
9
|
+
module ActiveRecord
|
10
|
+
module ConnectionAdapters
|
11
|
+
module Spanner
|
12
|
+
class Column < ConnectionAdapters::Column
|
13
|
+
def has_default? # rubocop:disable Naming/PredicateName
|
14
|
+
super && !virtual?
|
15
|
+
end
|
16
|
+
|
17
|
+
def virtual?
|
18
|
+
sql_type_metadata.generated
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -35,7 +35,10 @@ module ActiveRecord
|
|
35
35
|
binds.delete staleness_hint
|
36
36
|
end
|
37
37
|
|
38
|
-
|
38
|
+
log_args = [sql, name]
|
39
|
+
log_args.concat [binds, type_casted_binds(binds)] if log_statement_binds
|
40
|
+
|
41
|
+
log(*log_args) do
|
39
42
|
types, params = to_types_and_params binds
|
40
43
|
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
41
44
|
if transaction_required
|
@@ -219,9 +222,9 @@ module ActiveRecord
|
|
219
222
|
end.to_h
|
220
223
|
params = binds.enum_for(:each_with_index).map do |bind, i|
|
221
224
|
type = bind.respond_to?(:type) ? bind.type : ActiveModel::Type::Integer
|
222
|
-
|
223
|
-
value =
|
224
|
-
|
225
|
+
bind_value = bind.respond_to?(:value) ? bind.value : bind
|
226
|
+
value = ActiveRecord::Type::Spanner::SpannerActiveRecordConverter
|
227
|
+
.serialize_with_transaction_isolation_level(type, bind_value, :dml)
|
225
228
|
|
226
229
|
["p#{i + 1}", value]
|
227
230
|
end.to_h
|
@@ -10,7 +10,8 @@ module ActiveRecord
|
|
10
10
|
class SchemaCreation < SchemaCreation
|
11
11
|
private
|
12
12
|
|
13
|
-
# rubocop:disable Naming/MethodName, Metrics/AbcSize, Metrics/PerceivedComplexity
|
13
|
+
# rubocop:disable Naming/MethodName, Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
14
|
+
# rubocop:disable Metrics/MethodLength
|
14
15
|
|
15
16
|
def visit_TableDefinition o
|
16
17
|
create_sql = +"CREATE TABLE #{quote_table_name o.name} "
|
@@ -26,6 +27,14 @@ module ActiveRecord
|
|
26
27
|
end
|
27
28
|
end
|
28
29
|
|
30
|
+
if ActiveRecord::VERSION::MAJOR >= 7
|
31
|
+
statements.concat(o.check_constraints.map { |chk| accept chk })
|
32
|
+
elsif ActiveRecord::VERSION::MAJOR == 6 && ActiveRecord::VERSION::MINOR >= 1
|
33
|
+
statements.concat(
|
34
|
+
o.check_constraints.map { |expression, options| check_constraint_in_create o.name, expression, options }
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
29
38
|
create_sql << "(#{statements.join ', '}) " if statements.any?
|
30
39
|
|
31
40
|
primary_keys = if o.primary_keys
|
@@ -63,7 +72,7 @@ module ActiveRecord
|
|
63
72
|
def visit_ColumnDefinition o
|
64
73
|
o.sql_type = type_to_sql o.type, **o.options
|
65
74
|
column_sql = +"#{quote_column_name o.name} #{o.sql_type}"
|
66
|
-
add_column_options! column_sql, column_options(o)
|
75
|
+
add_column_options! o, column_sql, column_options(o)
|
67
76
|
column_sql
|
68
77
|
end
|
69
78
|
|
@@ -112,12 +121,16 @@ module ActiveRecord
|
|
112
121
|
sql
|
113
122
|
end
|
114
123
|
|
115
|
-
# rubocop:enable Naming/MethodName, Metrics/AbcSize, Metrics/PerceivedComplexity
|
124
|
+
# rubocop:enable Naming/MethodName, Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
125
|
+
# rubocop:enable Metrics/MethodLength
|
116
126
|
|
117
|
-
def add_column_options! sql, options
|
127
|
+
def add_column_options! column, sql, options
|
118
128
|
if options[:null] == false || options[:primary_key] == true
|
119
129
|
sql << " NOT NULL"
|
120
130
|
end
|
131
|
+
if options.key? :default
|
132
|
+
sql << " DEFAULT #{quote_default_expression options[:default], column}"
|
133
|
+
end
|
121
134
|
|
122
135
|
if !options[:allow_commit_timestamp].nil? &&
|
123
136
|
options[:column].sql_type == "TIMESTAMP"
|
@@ -15,8 +15,8 @@ module ActiveRecord
|
|
15
15
|
@on_delete = on_delete
|
16
16
|
end
|
17
17
|
|
18
|
-
def parent_key name
|
19
|
-
column name, :parent_key, null: false
|
18
|
+
def parent_key name, type: nil
|
19
|
+
column name, :parent_key, null: false, passed_type: type
|
20
20
|
end
|
21
21
|
|
22
22
|
def interleave_in?
|
@@ -33,6 +33,15 @@ module ActiveRecord
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
alias belongs_to references
|
36
|
+
|
37
|
+
def new_column_definition name, type, **options
|
38
|
+
case type
|
39
|
+
when :virtual
|
40
|
+
type = options[:type]
|
41
|
+
end
|
42
|
+
|
43
|
+
super
|
44
|
+
end
|
36
45
|
end
|
37
46
|
|
38
47
|
class Table < ActiveRecord::ConnectionAdapters::Table
|
@@ -13,6 +13,62 @@ module ActiveRecord
|
|
13
13
|
def default_primary_key? column
|
14
14
|
schema_type(column) == :integer
|
15
15
|
end
|
16
|
+
|
17
|
+
def prepare_column_options column
|
18
|
+
spec = super
|
19
|
+
|
20
|
+
unless column.sql_type_metadata.allow_commit_timestamp.nil?
|
21
|
+
spec[:allow_commit_timestamp] = column.sql_type_metadata.allow_commit_timestamp
|
22
|
+
end
|
23
|
+
|
24
|
+
if column.virtual?
|
25
|
+
spec[:as] = extract_expression_for_virtual_column column
|
26
|
+
spec[:stored] = true
|
27
|
+
spec = { type: schema_type(column).inspect }.merge! spec
|
28
|
+
end
|
29
|
+
|
30
|
+
spec
|
31
|
+
end
|
32
|
+
|
33
|
+
def header stream
|
34
|
+
str = StringIO.new
|
35
|
+
super str
|
36
|
+
stream.puts <<~HEADER
|
37
|
+
#{str.string.rstrip}
|
38
|
+
connection.start_batch_ddl
|
39
|
+
|
40
|
+
HEADER
|
41
|
+
end
|
42
|
+
|
43
|
+
def trailer stream
|
44
|
+
stream.puts <<~TRAILER
|
45
|
+
connection.run_batch
|
46
|
+
rescue
|
47
|
+
abort_batch
|
48
|
+
raise
|
49
|
+
TRAILER
|
50
|
+
super
|
51
|
+
end
|
52
|
+
|
53
|
+
def index_parts index
|
54
|
+
index_parts = super
|
55
|
+
index_parts << "null_filtered: #{index.null_filtered.inspect}" if index.null_filtered
|
56
|
+
index_parts << "interleave_in: #{index.interleave_in.inspect}" if index.interleave_in
|
57
|
+
index_parts << "storing: #{format_index_parts index.storing}" if index.storing.present?
|
58
|
+
index_parts
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def column_spec_for_primary_key column
|
64
|
+
spec = super
|
65
|
+
spec.except! :limit if default_primary_key? column
|
66
|
+
spec
|
67
|
+
end
|
68
|
+
|
69
|
+
def extract_expression_for_virtual_column column
|
70
|
+
column.default_function.inspect
|
71
|
+
end
|
16
72
|
end
|
17
73
|
end
|
18
74
|
end
|
@@ -8,6 +8,7 @@
|
|
8
8
|
|
9
9
|
require "active_record/connection_adapters/spanner/schema_creation"
|
10
10
|
require "active_record/connection_adapters/spanner/schema_dumper"
|
11
|
+
require "active_record/connection_adapters/spanner/column"
|
11
12
|
|
12
13
|
module ActiveRecord
|
13
14
|
module ConnectionAdapters
|
@@ -39,18 +40,19 @@ module ActiveRecord
|
|
39
40
|
end
|
40
41
|
alias data_source_exists? table_exists?
|
41
42
|
|
42
|
-
def create_table table_name, **options
|
43
|
+
def create_table table_name, id: :primary_key, **options
|
43
44
|
td = create_table_definition table_name, options
|
44
45
|
|
45
|
-
if
|
46
|
+
if id
|
46
47
|
pk = options.fetch :primary_key do
|
47
48
|
Base.get_primary_key table_name.to_s.singularize
|
48
49
|
end
|
50
|
+
id = id.fetch :type, :primary_key if id.is_a? Hash
|
49
51
|
|
50
52
|
if pk.is_a? Array
|
51
53
|
td.primary_keys pk
|
52
54
|
else
|
53
|
-
td.primary_key pk,
|
55
|
+
td.primary_key pk, id, **{}
|
54
56
|
end
|
55
57
|
end
|
56
58
|
|
@@ -108,16 +110,23 @@ module ActiveRecord
|
|
108
110
|
end
|
109
111
|
|
110
112
|
def new_column_from_field _table_name, field
|
111
|
-
|
113
|
+
Spanner::Column.new \
|
112
114
|
field.name,
|
113
115
|
field.default,
|
114
|
-
fetch_type_metadata(field.spanner_type,
|
115
|
-
|
116
|
+
fetch_type_metadata(field.spanner_type,
|
117
|
+
field.ordinal_position,
|
118
|
+
field.allow_commit_timestamp,
|
119
|
+
field.generated),
|
120
|
+
field.nullable,
|
121
|
+
field.default_function
|
116
122
|
end
|
117
123
|
|
118
|
-
def fetch_type_metadata sql_type, ordinal_position = nil
|
124
|
+
def fetch_type_metadata sql_type, ordinal_position = nil, allow_commit_timestamp = nil, generated = nil
|
119
125
|
Spanner::TypeMetadata.new \
|
120
|
-
super(sql_type),
|
126
|
+
super(sql_type),
|
127
|
+
ordinal_position: ordinal_position,
|
128
|
+
allow_commit_timestamp: allow_commit_timestamp,
|
129
|
+
generated: generated
|
121
130
|
end
|
122
131
|
|
123
132
|
def add_column table_name, column_name, type, **options
|
@@ -376,6 +385,44 @@ module ActiveRecord
|
|
376
385
|
end
|
377
386
|
alias add_belongs_to add_reference
|
378
387
|
|
388
|
+
# Check Contraints
|
389
|
+
|
390
|
+
def check_constraints table_name
|
391
|
+
information_schema { |i| i.check_constraints table_name }
|
392
|
+
end
|
393
|
+
|
394
|
+
def assume_migrated_upto_version version
|
395
|
+
version = version.to_i
|
396
|
+
sm_table = quote_table_name schema_migration.table_name
|
397
|
+
|
398
|
+
migrated = migration_context.get_all_versions
|
399
|
+
versions = migration_context.migrations.map(&:version)
|
400
|
+
|
401
|
+
execute "INSERT INTO #{sm_table} (version) VALUES (#{quote version.to_s})" unless migrated.include? version
|
402
|
+
|
403
|
+
inserting = (versions - migrated).select { |v| v < version }
|
404
|
+
return unless inserting.any?
|
405
|
+
|
406
|
+
if (duplicate = inserting.detect { |v| inserting.count(v) > 1 })
|
407
|
+
raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
|
408
|
+
end
|
409
|
+
|
410
|
+
execute insert_versions_sql(inserting)
|
411
|
+
end
|
412
|
+
|
413
|
+
def insert_versions_sql versions
|
414
|
+
sm_table = quote_table_name schema_migration.table_name
|
415
|
+
|
416
|
+
if versions.is_a? Array
|
417
|
+
sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
|
418
|
+
sql << versions.reverse.map { |v| "(#{quote v.to_s})" }.join(",\n")
|
419
|
+
sql << ";"
|
420
|
+
sql
|
421
|
+
else
|
422
|
+
"INSERT INTO #{sm_table} (version) VALUES (#{quote versions.to_s});"
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
379
426
|
def quoted_scope name = nil, type: nil
|
380
427
|
scope = { schema: quote("") }
|
381
428
|
scope[:name] = quote name if name
|
@@ -389,7 +436,7 @@ module ActiveRecord
|
|
389
436
|
|
390
437
|
# rubocop:disable Lint/UnusedMethodArgument
|
391
438
|
def type_to_sql type, limit: nil, precision: nil, scale: nil, **opts
|
392
|
-
type =
|
439
|
+
type = opts[:passed_type] || type&.to_sym
|
393
440
|
native = native_database_types[type]
|
394
441
|
|
395
442
|
return type.to_s unless native
|
@@ -12,24 +12,39 @@ module ActiveRecord
|
|
12
12
|
class TypeMetadata < DelegateClass(SqlTypeMetadata)
|
13
13
|
undef to_yaml if method_defined? :to_yaml
|
14
14
|
|
15
|
-
|
15
|
+
include Deduplicable if defined?(Deduplicable)
|
16
16
|
|
17
|
-
|
17
|
+
attr_reader :ordinal_position, :allow_commit_timestamp, :generated
|
18
|
+
|
19
|
+
def initialize type_metadata, ordinal_position: nil, allow_commit_timestamp: nil, generated: nil
|
18
20
|
super type_metadata
|
19
21
|
@ordinal_position = ordinal_position
|
22
|
+
@allow_commit_timestamp = allow_commit_timestamp
|
23
|
+
@generated = generated
|
20
24
|
end
|
21
25
|
|
22
26
|
def == other
|
23
27
|
other.is_a?(TypeMetadata) &&
|
24
28
|
__getobj__ == other.__getobj__ &&
|
25
|
-
ordinal_position == other.ordinal_position
|
29
|
+
ordinal_position == other.ordinal_position &&
|
30
|
+
allow_commit_timestamp == other.allow_commit_timestamp &&
|
31
|
+
generated == other.generated
|
26
32
|
end
|
27
33
|
alias eql? ==
|
28
34
|
|
29
35
|
def hash
|
30
36
|
TypeMetadata.hash ^
|
31
37
|
__getobj__.hash ^
|
32
|
-
ordinal_position.hash
|
38
|
+
ordinal_position.hash ^
|
39
|
+
allow_commit_timestamp.hash ^
|
40
|
+
generated.hash
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def deduplicated
|
46
|
+
__setobj__ __getobj__.deduplicate
|
47
|
+
super
|
33
48
|
end
|
34
49
|
end
|
35
50
|
end
|
@@ -74,6 +74,9 @@ module ActiveRecord
|
|
74
74
|
include Spanner::DatabaseStatements
|
75
75
|
include Spanner::SchemaStatements
|
76
76
|
|
77
|
+
# Determines whether or not to log query binds when executing statements
|
78
|
+
class_attribute :log_statement_binds, instance_writer: false, default: false
|
79
|
+
|
77
80
|
def initialize connection, logger, connection_options, config
|
78
81
|
super connection, logger, config
|
79
82
|
@connection_options = connection_options
|
@@ -170,6 +173,14 @@ module ActiveRecord
|
|
170
173
|
true
|
171
174
|
end
|
172
175
|
|
176
|
+
def supports_check_constraints?
|
177
|
+
true
|
178
|
+
end
|
179
|
+
|
180
|
+
def supports_virtual_columns?
|
181
|
+
true
|
182
|
+
end
|
183
|
+
|
173
184
|
# Generate next sequence number for primary key
|
174
185
|
def next_sequence_value _sequence_name
|
175
186
|
SecureRandom.uuid.gsub("-", "").hex & 0x7FFFFFFFFFFFFFFF
|
@@ -29,7 +29,12 @@ module ActiveRecord
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def purge
|
32
|
-
|
32
|
+
begin
|
33
|
+
drop
|
34
|
+
rescue ActiveRecord::NoDatabaseError # rubocop:disable Lint/HandleExceptions
|
35
|
+
# ignored; create the database
|
36
|
+
end
|
37
|
+
|
33
38
|
create
|
34
39
|
end
|
35
40
|
|
@@ -54,15 +59,24 @@ module ActiveRecord
|
|
54
59
|
next if ignore_tables.any? &&
|
55
60
|
(table_regx =~ statement || index_regx =~ statement)
|
56
61
|
file.write statement
|
57
|
-
file.write "
|
62
|
+
file.write ";\n"
|
58
63
|
end
|
59
64
|
ensure
|
60
65
|
file.close
|
61
66
|
end
|
62
67
|
|
63
68
|
def structure_load filename, _extra_flags
|
64
|
-
statements = File.read(filename).split(
|
65
|
-
|
69
|
+
statements = File.read(filename).split(/;/).map(&:strip).reject(&:empty?)
|
70
|
+
ddls = statements.select { |s| s =~ /^(CREATE|ALTER|DROP|GRANT|REVOKE|ANALYZE)/ }
|
71
|
+
@connection.execute_ddl ddls
|
72
|
+
|
73
|
+
client = @connection.spanner.client @connection.instance_id,
|
74
|
+
@connection.database_id
|
75
|
+
dmls = statements.reject { |s| s =~ /^(CREATE|ALTER|DROP|GRANT|REVOKE|ANALYZE)/ }
|
76
|
+
|
77
|
+
client.transaction do |tx|
|
78
|
+
dmls.each { |dml| tx.execute_query dml }
|
79
|
+
end
|
66
80
|
end
|
67
81
|
end
|
68
82
|
|
@@ -10,6 +10,16 @@ module ActiveRecord
|
|
10
10
|
module Type
|
11
11
|
module Spanner
|
12
12
|
class SpannerActiveRecordConverter
|
13
|
+
def self.serialize_with_transaction_isolation_level type, value, isolation_level
|
14
|
+
if type.respond_to? :serialize_with_isolation_level
|
15
|
+
type.serialize_with_isolation_level value, isolation_level
|
16
|
+
elsif type.respond_to? :serialize
|
17
|
+
type.serialize value
|
18
|
+
else
|
19
|
+
value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
13
23
|
##
|
14
24
|
# Converts an ActiveModel::Type to a Spanner type code.
|
15
25
|
def self.convert_active_model_type_to_spanner type # rubocop:disable Metrics/CyclomaticComplexity
|
@@ -10,9 +10,16 @@ module ActiveRecord
|
|
10
10
|
module Type
|
11
11
|
module Spanner
|
12
12
|
class Time < ActiveRecord::Type::Time
|
13
|
-
def
|
14
|
-
|
15
|
-
|
13
|
+
def serialize_with_isolation_level value, isolation_level
|
14
|
+
if value == :commit_timestamp
|
15
|
+
return "PENDING_COMMIT_TIMESTAMP()" if isolation_level == :dml
|
16
|
+
return "spanner.commit_timestamp()" if isolation_level == :mutation
|
17
|
+
end
|
18
|
+
|
19
|
+
serialize value
|
20
|
+
end
|
21
|
+
|
22
|
+
def serialize value
|
16
23
|
val = super value
|
17
24
|
val.acts_like?(:time) ? val.utc.rfc3339(9) : val
|
18
25
|
end
|