activerecord-spanner-adapter 1.2.1 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|