activerecord-spanner-adapter 1.6.0 → 1.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/rubocop.yaml +1 -1
- data/.release-please-manifest.json +1 -1
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +6 -0
- data/Gemfile +3 -1
- data/benchmarks/application.rb +1 -1
- data/examples/snippets/read-only-transactions/application.rb +1 -1
- data/lib/active_record/connection_adapters/spanner/schema_creation.rb +0 -2
- data/lib/active_record/connection_adapters/spanner/schema_statements.rb +9 -0
- data/lib/activerecord_spanner_adapter/base.rb +8 -1
- data/lib/activerecord_spanner_adapter/foreign_key.rb +6 -2
- data/lib/activerecord_spanner_adapter/index/column.rb +3 -1
- data/lib/activerecord_spanner_adapter/index.rb +4 -2
- data/lib/activerecord_spanner_adapter/information_schema.rb +94 -51
- data/lib/activerecord_spanner_adapter/table/column.rb +3 -1
- data/lib/activerecord_spanner_adapter/version.rb +1 -1
- data/lib/spanner_client_ext.rb +6 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 931e6b17bf3e6d6b068c57ca879bc0dbb06933790101aa05415514c6448fd004
|
4
|
+
data.tar.gz: c4981e814899be433830e0380f69060dfa589ec39dfa7e831aad616e224030ec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a989bdd8195d98206802a05294b09a53fe76c0684fba781d22dc15aaa5a38106ee3f193555cbe83e5f65254e3b39d95ed1cfb6445ded245ad80683d9752fdfd2
|
7
|
+
data.tar.gz: 0dbe1209ccad8da313fafb110bbb4fb2c1ef9ff9209230e88673f031ad02c0c372a13422fd2a5756fb0bc97a2318c003433350e071f64bcef37ba5a7754e868e
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
@@ -8,9 +8,11 @@ gem "minitest", "~> 5.20.0"
|
|
8
8
|
gem "minitest-rg", "~> 5.3.0"
|
9
9
|
gem "pry", "~> 0.13.0"
|
10
10
|
gem "pry-byebug", "~> 3.9.0"
|
11
|
+
# Add sqlite3 for testing for compatibility with other adapters.
|
12
|
+
gem "sqlite3"
|
11
13
|
|
12
14
|
# Required for samples and testing.
|
13
|
-
install_if -> { ENV.fetch("AR_VERSION", "~> 6.1.6.1").dup.to_s.sub
|
15
|
+
install_if -> { ENV.fetch("AR_VERSION", "~> 6.1.6.1").dup.to_s.sub("~>", "").strip < "7.1.0" && !ENV["SKIP_COMPOSITE_PK"] } do
|
14
16
|
gem "composite_primary_keys"
|
15
17
|
end
|
16
18
|
|
data/benchmarks/application.rb
CHANGED
@@ -12,7 +12,7 @@ require_relative "models/singer"
|
|
12
12
|
require_relative "models/album"
|
13
13
|
|
14
14
|
class Application
|
15
|
-
def self.run # rubocop:disable Metrics/AbcSize
|
15
|
+
def self.run # rubocop:disable Metrics/AbcSize
|
16
16
|
ActiveRecord::Base.logger.level = Logger::WARN
|
17
17
|
config = ActiveRecord::Base.connection_config
|
18
18
|
spanner = Google::Cloud::Spanner.new project: config[:project], credentials: config[:credentials]
|
@@ -10,7 +10,7 @@ require_relative "models/singer"
|
|
10
10
|
require_relative "models/album"
|
11
11
|
|
12
12
|
class Application
|
13
|
-
def self.run # rubocop:disable Metrics/AbcSize
|
13
|
+
def self.run # rubocop:disable Metrics/AbcSize
|
14
14
|
# Use a read-only transaction to execute multiple reads at the same commit timestamp.
|
15
15
|
# The Spanner ActiveRecord adapter supports the custom isolation level :read_only that
|
16
16
|
# will start a read-only Spanner transaction with a strong timestamp bound.
|
@@ -11,7 +11,6 @@ module ActiveRecord
|
|
11
11
|
private
|
12
12
|
|
13
13
|
# rubocop:disable Naming/MethodName, Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
14
|
-
# rubocop:disable Metrics/MethodLength
|
15
14
|
|
16
15
|
def visit_TableDefinition o
|
17
16
|
create_sql = +"CREATE TABLE #{quote_table_name o.name} "
|
@@ -133,7 +132,6 @@ module ActiveRecord
|
|
133
132
|
end
|
134
133
|
|
135
134
|
# rubocop:enable Naming/MethodName, Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
136
|
-
# rubocop:enable Metrics/MethodLength
|
137
135
|
|
138
136
|
def add_column_options! column, sql, options
|
139
137
|
if options[:null] == false || options[:primary_key] == true
|
@@ -40,6 +40,15 @@ module ActiveRecord
|
|
40
40
|
end
|
41
41
|
alias data_source_exists? table_exists?
|
42
42
|
|
43
|
+
def extract_schema_qualified_name string
|
44
|
+
schema, name = string.to_s.scan(/[^`.\s]+|`[^`]*`/)
|
45
|
+
unless name
|
46
|
+
name = schema
|
47
|
+
schema = nil
|
48
|
+
end
|
49
|
+
[schema, name]
|
50
|
+
end
|
51
|
+
|
43
52
|
def create_table table_name, id: :primary_key, **options
|
44
53
|
td = create_table_definition table_name, options
|
45
54
|
|
@@ -49,7 +49,10 @@ module ActiveRecord
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def self._insert_record values, returning = []
|
52
|
-
|
52
|
+
if !(buffered_mutations? || (primary_key && values.is_a?(Hash))) || !spanner_adapter?
|
53
|
+
return super values if ActiveRecord.gem_version < VERSION_7_1
|
54
|
+
return super
|
55
|
+
end
|
53
56
|
|
54
57
|
# Mutations cannot be used in combination with a sequence, as mutations do not support a THEN RETURN clause.
|
55
58
|
if buffered_mutations? && sequence_name
|
@@ -59,6 +62,10 @@ module ActiveRecord
|
|
59
62
|
|
60
63
|
return _buffer_record values, :insert, returning if buffered_mutations?
|
61
64
|
|
65
|
+
_insert_record_dml values, returning
|
66
|
+
end
|
67
|
+
|
68
|
+
def self._insert_record_dml values, returning
|
62
69
|
primary_key_value = _set_primary_key_value values
|
63
70
|
if ActiveRecord::VERSION::MAJOR >= 7
|
64
71
|
im = Arel::InsertManager.new arel_table
|
@@ -6,7 +6,7 @@
|
|
6
6
|
|
7
7
|
module ActiveRecordSpannerAdapter
|
8
8
|
class ForeignKey
|
9
|
-
attr_accessor :table_name, :name, :columns, :ref_table, :ref_columns,
|
9
|
+
attr_accessor :table_schema, :table_name, :name, :columns, :ref_schema, :ref_table, :ref_columns,
|
10
10
|
:on_delete, :on_update
|
11
11
|
|
12
12
|
def initialize \
|
@@ -16,10 +16,14 @@ module ActiveRecordSpannerAdapter
|
|
16
16
|
ref_table,
|
17
17
|
ref_columns,
|
18
18
|
on_delete: nil,
|
19
|
-
on_update: nil
|
19
|
+
on_update: nil,
|
20
|
+
table_schema: "",
|
21
|
+
ref_schema: ""
|
22
|
+
@table_schema = table_schema
|
20
23
|
@table_name = table_name
|
21
24
|
@name = name
|
22
25
|
@columns = Array(columns)
|
26
|
+
@ref_schema = ref_schema
|
23
27
|
@ref_table = ref_table
|
24
28
|
@ref_columns = Array(ref_columns)
|
25
29
|
@on_delete = on_delete unless on_delete == "NO ACTION"
|
@@ -7,16 +7,18 @@
|
|
7
7
|
module ActiveRecordSpannerAdapter
|
8
8
|
class Index
|
9
9
|
class Column
|
10
|
-
attr_accessor :table_name, :index_name, :name, :order, :ordinal_position
|
10
|
+
attr_accessor :table_name, :schema_name, :index_name, :name, :order, :ordinal_position
|
11
11
|
|
12
12
|
def initialize \
|
13
13
|
table_name,
|
14
14
|
index_name,
|
15
15
|
name,
|
16
|
+
schema_name: "",
|
16
17
|
order: nil,
|
17
18
|
ordinal_position: nil
|
18
19
|
@table_name = table_name.to_s
|
19
20
|
@index_name = index_name.to_s
|
21
|
+
@schema_name = schema_name.to_s
|
20
22
|
@name = name.to_s
|
21
23
|
@order = order.to_s.upcase if order
|
22
24
|
@ordinal_position = ordinal_position
|
@@ -8,7 +8,7 @@ require "activerecord_spanner_adapter/index/column"
|
|
8
8
|
|
9
9
|
module ActiveRecordSpannerAdapter
|
10
10
|
class Index
|
11
|
-
attr_accessor :table, :name, :columns, :type, :unique, :null_filtered,
|
11
|
+
attr_accessor :schema, :table, :name, :columns, :type, :unique, :null_filtered,
|
12
12
|
:interleave_in, :storing, :state
|
13
13
|
|
14
14
|
def initialize \
|
@@ -20,7 +20,9 @@ module ActiveRecordSpannerAdapter
|
|
20
20
|
null_filtered: false,
|
21
21
|
interleave_in: nil,
|
22
22
|
storing: nil,
|
23
|
-
state: nil
|
23
|
+
state: nil,
|
24
|
+
schema: ""
|
25
|
+
@schema = schema.to_s
|
24
26
|
@table = table.to_s
|
25
27
|
@name = name.to_s
|
26
28
|
@columns = Array(columns)
|
@@ -24,7 +24,7 @@ module ActiveRecordSpannerAdapter
|
|
24
24
|
@mutex = Mutex.new
|
25
25
|
end
|
26
26
|
|
27
|
-
def tables table_name: nil, schema_name:
|
27
|
+
def tables table_name: nil, schema_name: "", view: nil
|
28
28
|
sql = +"SELECT TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, PARENT_TABLE_NAME, ON_DELETE_ACTION"
|
29
29
|
sql << " FROM INFORMATION_SCHEMA.TABLES"
|
30
30
|
sql << " WHERE TABLE_SCHEMA=%<schema_name>s"
|
@@ -45,17 +45,17 @@ module ActiveRecordSpannerAdapter
|
|
45
45
|
)
|
46
46
|
|
47
47
|
if [:full, :columns].include? view
|
48
|
-
table.columns = table_columns table.name
|
48
|
+
table.columns = table_columns table.name, schema_name: schema_name
|
49
49
|
end
|
50
50
|
|
51
51
|
if [:full, :indexes].include? view
|
52
|
-
table.indexes = indexes table.name
|
52
|
+
table.indexes = indexes table.name, schema_name: table.schema_name
|
53
53
|
end
|
54
54
|
table
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
-
def table table_name, schema_name:
|
58
|
+
def table table_name, schema_name: "", view: nil
|
59
59
|
tables(
|
60
60
|
table_name: table_name,
|
61
61
|
schema_name: schema_name,
|
@@ -63,26 +63,28 @@ module ActiveRecordSpannerAdapter
|
|
63
63
|
).first
|
64
64
|
end
|
65
65
|
|
66
|
-
def table_columns table_name, column_name: nil
|
66
|
+
def table_columns table_name, column_name: nil, schema_name: ""
|
67
67
|
primary_keys = table_primary_keys(table_name).map(&:name)
|
68
68
|
sql = +"SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION,"
|
69
69
|
sql << " CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION"
|
70
70
|
sql << " FROM INFORMATION_SCHEMA.COLUMNS"
|
71
71
|
sql << " WHERE TABLE_NAME=%<table_name>s"
|
72
|
+
sql << " AND TABLE_SCHEMA=%<schema_name>s"
|
72
73
|
sql << " AND COLUMN_NAME=%<column_name>s" if column_name
|
73
74
|
sql << " ORDER BY ORDINAL_POSITION ASC"
|
74
75
|
|
75
|
-
column_options = column_options table_name, column_name
|
76
|
+
column_options = column_options table_name, column_name, schema_name: schema_name
|
76
77
|
execute_query(
|
77
78
|
sql,
|
78
79
|
table_name: table_name,
|
79
|
-
column_name: column_name
|
80
|
+
column_name: column_name,
|
81
|
+
schema_name: schema_name
|
80
82
|
).map do |row|
|
81
|
-
_create_column table_name, row, primary_keys, column_options
|
83
|
+
_create_column table_name, row, primary_keys, column_options, schema_name: schema_name
|
82
84
|
end
|
83
85
|
end
|
84
86
|
|
85
|
-
def _create_column table_name, row, primary_keys, column_options
|
87
|
+
def _create_column table_name, row, primary_keys, column_options, schema_name: ""
|
86
88
|
type, limit = parse_type_and_limit row["SPANNER_TYPE"]
|
87
89
|
column_name = row["COLUMN_NAME"]
|
88
90
|
options = column_options[column_name]
|
@@ -104,6 +106,7 @@ module ActiveRecordSpannerAdapter
|
|
104
106
|
table_name,
|
105
107
|
column_name,
|
106
108
|
type,
|
109
|
+
schema_name: schema_name,
|
107
110
|
limit: limit,
|
108
111
|
allow_commit_timestamp: options["allow_commit_timestamp"],
|
109
112
|
ordinal_position: row["ORDINAL_POSITION"],
|
@@ -114,8 +117,8 @@ module ActiveRecordSpannerAdapter
|
|
114
117
|
primary_key: primary_key
|
115
118
|
end
|
116
119
|
|
117
|
-
def table_column table_name, column_name
|
118
|
-
table_columns(table_name, column_name: column_name).first
|
120
|
+
def table_column table_name, column_name, schema_name: ""
|
121
|
+
table_columns(table_name, column_name: column_name, schema_name: schema_name).first
|
119
122
|
end
|
120
123
|
|
121
124
|
# Returns the primary key columns of the given table. By default it will only return the columns that are not part
|
@@ -123,43 +126,49 @@ module ActiveRecordSpannerAdapter
|
|
123
126
|
# ActiveRecord. The parent primary key columns are filtered out by default to allow interleaved tables to be
|
124
127
|
# considered as tables with a single-column primary key by ActiveRecord. The actual primary key of the table will
|
125
128
|
# include both the parent primary key columns and the 'own' primary key columns of a table.
|
126
|
-
def table_primary_keys table_name, include_parent_keys = IsRails71OrLater
|
129
|
+
def table_primary_keys table_name, include_parent_keys = IsRails71OrLater, schema_name: ""
|
127
130
|
sql = +"WITH TABLE_PK_COLS AS ( "
|
128
|
-
sql << "SELECT C.
|
131
|
+
sql << "SELECT C.TABLE_CATALOG, C.TABLE_SCHEMA, C.TABLE_NAME, C.COLUMN_NAME, "
|
132
|
+
sql << "C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION "
|
129
133
|
sql << "FROM INFORMATION_SCHEMA.INDEX_COLUMNS C "
|
130
134
|
sql << "WHERE C.INDEX_TYPE = 'PRIMARY_KEY' "
|
131
135
|
sql << "AND TABLE_CATALOG = '' "
|
132
136
|
sql << "AND TABLE_SCHEMA = '') "
|
133
137
|
sql << "SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION "
|
134
138
|
sql << "FROM TABLE_PK_COLS "
|
135
|
-
sql << "INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) "
|
139
|
+
sql << "INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME) "
|
136
140
|
sql << "WHERE TABLE_NAME = %<table_name>s "
|
137
141
|
sql << "AND TABLE_CATALOG = '' "
|
138
|
-
sql << "AND TABLE_SCHEMA =
|
142
|
+
sql << "AND TABLE_SCHEMA = %<schema_name>s "
|
139
143
|
unless include_parent_keys
|
140
144
|
sql << "AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( "
|
141
145
|
sql << " SELECT COLUMN_NAME "
|
142
146
|
sql << " FROM TABLE_PK_COLS "
|
143
|
-
sql << " WHERE
|
147
|
+
sql << " WHERE TABLE_CATALOG = T.TABLE_CATALOG "
|
148
|
+
sql << " AND TABLE_SCHEMA=T.TABLE_SCHEMA "
|
149
|
+
sql << " AND TABLE_NAME = T.PARENT_TABLE_NAME "
|
144
150
|
sql << ")) "
|
145
151
|
end
|
146
152
|
sql << "ORDER BY ORDINAL_POSITION"
|
147
153
|
execute_query(
|
148
154
|
sql,
|
149
|
-
table_name: table_name
|
155
|
+
table_name: table_name,
|
156
|
+
schema_name: schema_name
|
150
157
|
).map do |row|
|
151
158
|
Index::Column.new \
|
152
159
|
table_name,
|
153
160
|
row["INDEX_NAME"],
|
154
161
|
row["COLUMN_NAME"],
|
162
|
+
schema_name: schema_name,
|
155
163
|
order: row["COLUMN_ORDERING"],
|
156
164
|
ordinal_position: row["ORDINAL_POSITION"]
|
157
165
|
end
|
158
166
|
end
|
159
167
|
|
160
|
-
def indexes table_name, index_name: nil, index_type: nil
|
168
|
+
def indexes table_name, schema_name: "", index_name: nil, index_type: nil
|
161
169
|
table_indexes_columns = index_columns(
|
162
170
|
table_name,
|
171
|
+
schema_name: schema_name,
|
163
172
|
index_name: index_name
|
164
173
|
)
|
165
174
|
|
@@ -167,7 +176,7 @@ module ActiveRecordSpannerAdapter
|
|
167
176
|
sql << " FROM INFORMATION_SCHEMA.INDEXES"
|
168
177
|
sql << " WHERE TABLE_NAME=%<table_name>s"
|
169
178
|
sql << " AND TABLE_CATALOG = ''"
|
170
|
-
sql << " AND TABLE_SCHEMA =
|
179
|
+
sql << " AND TABLE_SCHEMA = %<schema_name>s"
|
171
180
|
sql << " AND INDEX_NAME=%<index_name>s" if index_name
|
172
181
|
sql << " AND INDEX_TYPE=%<index_type>s" if index_type
|
173
182
|
sql << " AND SPANNER_IS_MANAGED=FALSE"
|
@@ -175,6 +184,7 @@ module ActiveRecordSpannerAdapter
|
|
175
184
|
execute_query(
|
176
185
|
sql,
|
177
186
|
table_name: table_name,
|
187
|
+
schema_name: schema_name,
|
178
188
|
index_name: index_name,
|
179
189
|
index_type: index_type
|
180
190
|
).map do |row|
|
@@ -198,89 +208,120 @@ module ActiveRecordSpannerAdapter
|
|
198
208
|
null_filtered: row["IS_NULL_FILTERED"],
|
199
209
|
interleave_in: row["PARENT_TABLE_NAME"],
|
200
210
|
storing: storing,
|
201
|
-
state: row["INDEX_STATE"]
|
211
|
+
state: row["INDEX_STATE"],
|
212
|
+
schema: schema_name
|
202
213
|
end
|
203
214
|
end
|
204
215
|
|
205
|
-
def index table_name, index_name
|
206
|
-
indexes(table_name, index_name: index_name).first
|
216
|
+
def index table_name, index_name, schema_name: ""
|
217
|
+
indexes(table_name, index_name: index_name, schema_name: schema_name).first
|
207
218
|
end
|
208
219
|
|
209
|
-
def index_columns table_name, index_name: nil
|
220
|
+
def index_columns table_name, schema_name: "", index_name: nil
|
210
221
|
sql = +"SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION"
|
211
222
|
sql << " FROM INFORMATION_SCHEMA.INDEX_COLUMNS"
|
212
223
|
sql << " WHERE TABLE_NAME=%<table_name>s"
|
213
224
|
sql << " AND TABLE_CATALOG = ''"
|
214
|
-
sql << " AND TABLE_SCHEMA =
|
225
|
+
sql << " AND TABLE_SCHEMA = %<schema_name>s"
|
215
226
|
sql << " AND INDEX_NAME=%<index_name>s" if index_name
|
216
227
|
sql << " ORDER BY ORDINAL_POSITION ASC"
|
217
228
|
|
218
229
|
execute_query(
|
219
230
|
sql,
|
220
|
-
table_name: table_name, index_name: index_name
|
231
|
+
table_name: table_name, schema_name: schema_name, index_name: index_name
|
221
232
|
).map do |row|
|
222
233
|
Index::Column.new \
|
223
234
|
table_name,
|
224
235
|
row["INDEX_NAME"],
|
225
236
|
row["COLUMN_NAME"],
|
237
|
+
schema_name: schema_name,
|
226
238
|
order: row["COLUMN_ORDERING"],
|
227
239
|
ordinal_position: row["ORDINAL_POSITION"]
|
228
240
|
end
|
229
241
|
end
|
230
242
|
|
231
|
-
def indexes_by_columns table_name, column_names
|
243
|
+
def indexes_by_columns table_name, column_names, schema_name: ""
|
232
244
|
column_names = Array(column_names).map(&:to_s)
|
233
245
|
|
234
|
-
indexes(table_name).select do |index|
|
246
|
+
indexes(table_name, schema_name: schema_name).select do |index|
|
235
247
|
index.columns.any? { |c| column_names.include? c.name }
|
236
248
|
end
|
237
249
|
end
|
238
250
|
|
239
|
-
def foreign_keys
|
251
|
+
def foreign_keys from_table_name, from_schema_name: ""
|
240
252
|
sql = <<~SQL
|
241
|
-
SELECT
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
253
|
+
SELECT CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, UPDATE_RULE, DELETE_RULE,
|
254
|
+
FK_CATALOG, FK_SCHEMA, FK_TABLE,
|
255
|
+
PK_CATALOG, PK_SCHEMA, PK_TABLE,
|
256
|
+
ARRAY_AGG(FK_COLUMN) AS FK_COLUMNS, ARRAY_AGG(PK_COLUMN) AS PK_COLUMNS
|
257
|
+
FROM (SELECT CONSTRAINTS.CONSTRAINT_CATALOG,
|
258
|
+
CONSTRAINTS.CONSTRAINT_SCHEMA,
|
259
|
+
CONSTRAINTS.CONSTRAINT_NAME,
|
260
|
+
CONSTRAINTS.UPDATE_RULE,
|
261
|
+
CONSTRAINTS.DELETE_RULE,
|
262
|
+
CHILD.TABLE_CATALOG AS FK_CATALOG,
|
263
|
+
CHILD.TABLE_SCHEMA AS FK_SCHEMA,
|
264
|
+
CHILD.TABLE_NAME AS FK_TABLE,
|
265
|
+
CHILD.COLUMN_NAME AS FK_COLUMN,
|
266
|
+
PARENT.TABLE_CATALOG AS PK_CATALOG,
|
267
|
+
PARENT.TABLE_SCHEMA AS PK_SCHEMA,
|
268
|
+
PARENT.TABLE_NAME AS PK_TABLE,
|
269
|
+
PARENT.COLUMN_NAME AS PK_COLUMN
|
270
|
+
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS CONSTRAINTS
|
271
|
+
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CHILD
|
272
|
+
ON CONSTRAINTS.CONSTRAINT_CATALOG = CHILD.CONSTRAINT_CATALOG
|
273
|
+
AND CONSTRAINTS.CONSTRAINT_SCHEMA = CHILD.CONSTRAINT_SCHEMA
|
274
|
+
AND CONSTRAINTS.CONSTRAINT_NAME = CHILD.CONSTRAINT_NAME
|
275
|
+
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE PARENT
|
276
|
+
ON CONSTRAINTS.UNIQUE_CONSTRAINT_CATALOG = PARENT.CONSTRAINT_CATALOG
|
277
|
+
AND CONSTRAINTS.UNIQUE_CONSTRAINT_SCHEMA = PARENT.CONSTRAINT_SCHEMA
|
278
|
+
AND CONSTRAINTS.UNIQUE_CONSTRAINT_NAME = PARENT.CONSTRAINT_NAME
|
279
|
+
AND PARENT.ORDINAL_POSITION = CHILD.POSITION_IN_UNIQUE_CONSTRAINT
|
280
|
+
ORDER BY CHILD.TABLE_CATALOG, CHILD.TABLE_SCHEMA, CHILD.TABLE_NAME, CHILD.POSITION_IN_UNIQUE_CONSTRAINT
|
281
|
+
) FOREIGN_KEYS
|
282
|
+
WHERE FK_TABLE = %<table_name>s
|
283
|
+
AND FK_SCHEMA = %<constraint_schema>s
|
284
|
+
GROUP BY CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, UPDATE_RULE, DELETE_RULE,
|
285
|
+
FK_CATALOG, FK_SCHEMA, FK_TABLE,
|
286
|
+
PK_CATALOG, PK_SCHEMA, PK_TABLE
|
252
287
|
SQL
|
253
288
|
|
254
289
|
rows = execute_query(
|
255
|
-
sql, table_name:
|
290
|
+
sql, table_name: from_table_name, constraint_schema: from_schema_name
|
256
291
|
)
|
257
292
|
|
258
293
|
rows.map do |row|
|
259
294
|
ForeignKey.new(
|
260
|
-
|
261
|
-
row["
|
262
|
-
row["
|
263
|
-
row["
|
264
|
-
row["
|
265
|
-
on_delete: row["
|
266
|
-
on_update: row["
|
295
|
+
from_table_name,
|
296
|
+
row["CONSTRAINT_NAME"],
|
297
|
+
row["FK_COLUMNS"],
|
298
|
+
row["PK_TABLE"],
|
299
|
+
row["PK_COLUMNS"],
|
300
|
+
on_delete: row["DELETE_RULE"],
|
301
|
+
on_update: row["UPDATE_RULE"],
|
302
|
+
table_schema: from_schema_name,
|
303
|
+
ref_schema: row["PK_SCHEMA"]
|
267
304
|
)
|
268
305
|
end
|
269
306
|
end
|
270
307
|
|
271
|
-
def check_constraints table_name
|
308
|
+
def check_constraints table_name, schema_name: ""
|
272
309
|
sql = <<~SQL.squish
|
273
310
|
SELECT tc.TABLE_NAME,
|
274
311
|
tc.CONSTRAINT_NAME,
|
275
312
|
cc.CHECK_CLAUSE
|
276
313
|
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
|
277
|
-
INNER JOIN INFORMATION_SCHEMA.CHECK_CONSTRAINTS cc
|
314
|
+
INNER JOIN INFORMATION_SCHEMA.CHECK_CONSTRAINTS cc
|
315
|
+
ON tc.CONSTRAINT_CATALOG = cc.CONSTRAINT_CATALOG
|
316
|
+
AND tc.CONSTRAINT_SCHEMA = cc.CONSTRAINT_SCHEMA
|
317
|
+
AND tc.CONSTRAINT_NAME = cc.CONSTRAINT_NAME
|
278
318
|
WHERE tc.TABLE_NAME = %<table_name>s
|
319
|
+
AND tc.CONSTRAINT_SCHEMA = %<schema_name>s
|
279
320
|
AND tc.CONSTRAINT_TYPE = 'CHECK'
|
280
321
|
AND NOT (tc.CONSTRAINT_NAME LIKE 'CK_IS_NOT_NULL_%%' AND cc.CHECK_CLAUSE LIKE '%%IS NOT NULL')
|
281
322
|
SQL
|
282
323
|
|
283
|
-
rows = execute_query sql, table_name: table_name
|
324
|
+
rows = execute_query sql, table_name: table_name, schema_name: schema_name
|
284
325
|
|
285
326
|
rows.map do |row|
|
286
327
|
ActiveRecord::ConnectionAdapters::CheckConstraintDefinition.new(
|
@@ -363,16 +404,18 @@ module ActiveRecordSpannerAdapter
|
|
363
404
|
[value[start...(start + length)].to_i(base)].pack "U"
|
364
405
|
end
|
365
406
|
|
366
|
-
def column_options table_name, column_name
|
407
|
+
def column_options table_name, column_name, schema_name: ""
|
367
408
|
sql = +"SELECT COLUMN_NAME, OPTION_NAME, OPTION_TYPE, OPTION_VALUE"
|
368
409
|
sql << " FROM INFORMATION_SCHEMA.COLUMN_OPTIONS"
|
369
410
|
sql << " WHERE TABLE_NAME=%<table_name>s"
|
411
|
+
sql << " AND TABLE_SCHEMA=%<schema_name>s"
|
370
412
|
sql << " AND COLUMN_NAME=%<column_name>s" if column_name
|
371
413
|
|
372
414
|
column_options = Hash.new { |h, k| h[k] = {} }
|
373
415
|
execute_query(
|
374
416
|
sql,
|
375
417
|
table_name: table_name,
|
418
|
+
schema_name: schema_name,
|
376
419
|
column_name: column_name
|
377
420
|
).each_with_object(column_options) do |row, options|
|
378
421
|
next unless row["OPTION_TYPE"] == "BOOL"
|
@@ -7,7 +7,7 @@
|
|
7
7
|
module ActiveRecordSpannerAdapter
|
8
8
|
class Table
|
9
9
|
class Column
|
10
|
-
attr_accessor :table_name, :name, :type, :limit, :ordinal_position,
|
10
|
+
attr_accessor :schema_name, :table_name, :name, :type, :limit, :ordinal_position,
|
11
11
|
:allow_commit_timestamp, :default, :default_function, :generated,
|
12
12
|
:primary_key, :nullable
|
13
13
|
|
@@ -15,6 +15,7 @@ module ActiveRecordSpannerAdapter
|
|
15
15
|
table_name,
|
16
16
|
name,
|
17
17
|
type,
|
18
|
+
schema_name: "",
|
18
19
|
limit: nil,
|
19
20
|
ordinal_position: nil,
|
20
21
|
nullable: true,
|
@@ -23,6 +24,7 @@ module ActiveRecordSpannerAdapter
|
|
23
24
|
default_function: nil,
|
24
25
|
generated: nil,
|
25
26
|
primary_key: false
|
27
|
+
@schema_name = schema_name.to_s
|
26
28
|
@table_name = table_name.to_s
|
27
29
|
@name = name.to_s
|
28
30
|
@type = type
|
data/lib/spanner_client_ext.rb
CHANGED
@@ -68,7 +68,12 @@ module Google
|
|
68
68
|
snp_grpc = service.create_snapshot \
|
69
69
|
path, timestamp: (timestamp || read_timestamp),
|
70
70
|
staleness: (staleness || exact_staleness)
|
71
|
-
Snapshot.from_grpc
|
71
|
+
num_args = Snapshot.method(:from_grpc).arity
|
72
|
+
if num_args == 3
|
73
|
+
Snapshot.from_grpc snp_grpc, self, nil
|
74
|
+
else
|
75
|
+
Snapshot.from_grpc snp_grpc, self
|
76
|
+
end
|
72
77
|
end
|
73
78
|
|
74
79
|
def create_pdml
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-spanner-adapter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.6.
|
4
|
+
version: 1.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Google LLC
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-02-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: google-cloud-spanner
|
@@ -577,7 +577,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
577
577
|
- !ruby/object:Gem::Version
|
578
578
|
version: '0'
|
579
579
|
requirements: []
|
580
|
-
rubygems_version: 3.
|
580
|
+
rubygems_version: 3.5.3
|
581
581
|
signing_key:
|
582
582
|
specification_version: 4
|
583
583
|
summary: Rails ActiveRecord connector for Google Spanner Database
|