activerecord-spanner-adapter 1.6.0 → 1.6.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2ad2384bb1890bb224c89a146c42f615f89c6e2892848da5e701a091d90fa95b
4
- data.tar.gz: 0e8a9c608068ff30011bc9bca68b10ab0d22b910260a37a6d4a492365adf78b0
3
+ metadata.gz: 2daeef21393399532473625549d008d1a4cfcf791be1a347c98909c3e3973dde
4
+ data.tar.gz: 16caacb8cde7a9dfd2d2e22a8500d9a25b21f22fe392bb3c2631af892d35168a
5
5
  SHA512:
6
- metadata.gz: 1894765bbc1e3dfac270d73199a4f235ae95724ca18145d6850cce27c38ef2018b59e9035cf2fa91bc30d3f650e080b8eea58c3e91a66035db3a4d07c35ca1be
7
- data.tar.gz: 4a9b93d7990fa78f901864bed816e75ff5d0f9da34a530c7107f475d8fd06dc71817fbf8b2c1e0262e4656bf7ab363917ecf7b7c82315b6d5fa84cce0d22489d
6
+ metadata.gz: da334e1c10da6bbd60c6ececee046889b33e045df31838b8a0939f56cf93240b44c20002aa2b61bbcb6b961907b1f1ed73aa0fc9de8b1283faef427db84fce94
7
+ data.tar.gz: 52c05cfca494e311aa7049591a2258acf93880434d548834667742440ba9a7ddc283183dedb16227fc5b469fbc2b1cd50ed333c30f86e6f2fda0bf3ae1473e72
@@ -18,7 +18,7 @@ jobs:
18
18
  with:
19
19
  ruby-version: '2.7'
20
20
  - name: cache gems
21
- uses: actions/cache@v3
21
+ uses: actions/cache@v4
22
22
  with:
23
23
  path: vendor/bundle
24
24
  key: ${{ runner.os }}-rubocop-${{ hashFiles('**/Gemfile.lock') }}
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "1.6.0"
2
+ ".": "1.6.2"
3
3
  }
data/.rubocop.yml CHANGED
@@ -41,6 +41,6 @@ Style/WordArray:
41
41
  Style/RegexpLiteral:
42
42
  Enabled: false
43
43
  Metrics/MethodLength:
44
- Max: 40
44
+ Max: 60
45
45
  Metrics/BlockLength:
46
46
  Max: 30
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ### 1.6.2 (2024-02-19)
4
+
5
+ #### Bug Fixes
6
+
7
+ * failed to convert active model type to spanner type under certain condition ([#299](https://github.com/googleapis/ruby-spanner-activerecord/issues/299))
8
+
9
+ ### 1.6.1 (2024-02-05)
10
+
11
+ #### Bug Fixes
12
+
13
+ * _insert_record failed for other adapters ([#298](https://github.com/googleapis/ruby-spanner-activerecord/issues/298))
14
+
3
15
  ### 1.6.0 (2023-12-20)
4
16
 
5
17
  #### Features
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!("~>", "").strip < "7.1.0" && !ENV["SKIP_COMPOSITE_PK"] } do
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
 
@@ -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, Metrics/MethodLength
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, Metrics/MethodLength
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
 
@@ -23,6 +23,9 @@ module ActiveRecord
23
23
  ##
24
24
  # Converts an ActiveModel::Type to a Spanner type code.
25
25
  def self.convert_active_model_type_to_spanner type # rubocop:disable Metrics/CyclomaticComplexity
26
+ # Unwrap the underlying object if the type is a DelegateClass.
27
+ type = type.__getobj__ if type.respond_to? :__getobj__
28
+
26
29
  case type
27
30
  when NilClass then nil
28
31
  when ActiveModel::Type::Integer, ActiveModel::Type::BigInteger then :INT64
@@ -49,7 +49,10 @@ module ActiveRecord
49
49
  end
50
50
 
51
51
  def self._insert_record values, returning = []
52
- return super unless buffered_mutations? || (primary_key && values.is_a?(Hash))
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: nil, view: nil
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: nil, view: nil
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.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION "
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 TABLE_NAME = T.PARENT_TABLE_NAME "
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 table_name
251
+ def foreign_keys from_table_name, from_schema_name: ""
240
252
  sql = <<~SQL
241
- SELECT cc.table_name AS to_table,
242
- cc.column_name AS primary_key,
243
- fk.column_name as column,
244
- fk.constraint_name AS name,
245
- rc.update_rule AS on_update,
246
- rc.delete_rule AS on_delete
247
- FROM information_schema.referential_constraints rc
248
- INNER JOIN information_schema.key_column_usage fk ON rc.constraint_name = fk.constraint_name
249
- INNER JOIN information_schema.constraint_column_usage cc ON rc.constraint_name = cc.constraint_name
250
- WHERE fk.table_name = %<table_name>s
251
- AND fk.constraint_schema = %<constraint_schema>s
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: table_name, constraint_schema: ""
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
- table_name,
261
- row["name"],
262
- row["column"],
263
- row["to_table"],
264
- row["primary_key"],
265
- on_delete: row["on_delete"],
266
- on_update: row["on_update"]
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 ON tc.CONSTRAINT_NAME = cc.CONSTRAINT_NAME
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
@@ -5,5 +5,5 @@
5
5
  # https://opensource.org/licenses/MIT.
6
6
 
7
7
  module ActiveRecordSpannerAdapter
8
- VERSION = "1.6.0".freeze
8
+ VERSION = "1.6.2".freeze
9
9
  end
@@ -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 snp_grpc, self
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.0
4
+ version: 1.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Google LLC
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-12-20 00:00:00.000000000 Z
11
+ date: 2024-02-19 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.4.19
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