Skip to content

Commit f206247

Browse files
committed
feat: add support for the UUID data type
Adds support for the new Spanner data type `UUID`.
1 parent d1774f3 commit f206247

File tree

12 files changed

+166
-52
lines changed

12 files changed

+166
-52
lines changed

acceptance/cases/tasks/database_tasks_test.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ def expected_schema_sql_on_emulator
129129
col_date DATE,
130130
col_timestamp TIMESTAMP,
131131
col_json JSON,
132+
col_uuid UUID,
132133
col_array_string ARRAY<STRING(MAX)>,
133134
col_array_int64 ARRAY<INT64>,
134135
col_array_float64 ARRAY<FLOAT64>,
@@ -138,6 +139,7 @@ def expected_schema_sql_on_emulator
138139
col_array_date ARRAY<DATE>,
139140
col_array_timestamp ARRAY<TIMESTAMP>,
140141
col_array_json ARRAY<JSON>,
142+
col_array_uuid ARRAY<UUID>,
141143
) PRIMARY KEY(id);
142144
CREATE TABLE firms (
143145
id INT64 NOT NULL GENERATED BY DEFAULT AS IDENTITY (BIT_REVERSED_POSITIVE),
@@ -287,6 +289,7 @@ def expected_schema_sql_on_emulator_7_1
287289
col_date DATE,
288290
col_timestamp TIMESTAMP,
289291
col_json JSON,
292+
col_uuid UUID,
290293
col_array_string ARRAY<STRING(MAX)>,
291294
col_array_int64 ARRAY<INT64>,
292295
col_array_float64 ARRAY<FLOAT64>,
@@ -296,6 +299,7 @@ def expected_schema_sql_on_emulator_7_1
296299
col_array_date ARRAY<DATE>,
297300
col_array_timestamp ARRAY<TIMESTAMP>,
298301
col_array_json ARRAY<JSON>,
302+
col_array_uuid ARRAY<UUID>,
299303
) PRIMARY KEY(id);
300304
CREATE TABLE firms (
301305
id INT64 NOT NULL GENERATED BY DEFAULT AS IDENTITY (BIT_REVERSED_POSITIVE),
@@ -463,6 +467,7 @@ def expected_schema_sql_on_production
463467
col_date DATE,
464468
col_timestamp TIMESTAMP,
465469
col_json JSON,
470+
col_uuid UUID,
466471
col_array_string ARRAY<STRING(MAX)>,
467472
col_array_int64 ARRAY<INT64>,
468473
col_array_float64 ARRAY<FLOAT64>,
@@ -472,6 +477,7 @@ def expected_schema_sql_on_production
472477
col_array_date ARRAY<DATE>,
473478
col_array_timestamp ARRAY<TIMESTAMP>,
474479
col_array_json ARRAY<JSON>,
480+
col_array_uuid ARRAY<UUID>,
475481
) PRIMARY KEY(id);
476482
CREATE TABLE ar_internal_metadata (
477483
key STRING(MAX) NOT NULL,
@@ -624,6 +630,7 @@ def expected_schema_sql_on_production_7_1
624630
col_date DATE,
625631
col_timestamp TIMESTAMP,
626632
col_json JSON,
633+
col_uuid UUID,
627634
col_array_string ARRAY<STRING(MAX)>,
628635
col_array_int64 ARRAY<INT64>,
629636
col_array_float64 ARRAY<FLOAT64>,
@@ -633,6 +640,7 @@ def expected_schema_sql_on_production_7_1
633640
col_array_date ARRAY<DATE>,
634641
col_array_timestamp ARRAY<TIMESTAMP>,
635642
col_array_json ARRAY<JSON>,
643+
col_array_uuid ARRAY<UUID>,
636644
) PRIMARY KEY(id);
637645
CREATE TABLE ar_internal_metadata (
638646
key STRING(MAX) NOT NULL,

acceptance/cases/type/all_types_test.rb

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ def create_test_record
3030
AllTypes.create col_string: "string", col_int64: 100, col_float64: 3.14, col_numeric: 6.626, col_bool: true,
3131
col_bytes: StringIO.new("bytes"), col_date: ::Date.new(2021, 6, 23),
3232
col_timestamp: ::Time.new(2021, 6, 23, 17, 8, 21, "+02:00"),
33-
col_json: { kind: "user_renamed", change: %w[jack john]},
33+
col_json: { kind: "user_renamed", change: %w[jack john] },
34+
col_uuid: "3840ba25-55df-4cb7-a210-1fe37278954f",
3435
col_array_string: ["string1", nil, "string2"],
3536
col_array_int64: [100, nil, 200, "300"],
3637
col_array_float64: [3.14, nil, 2.0/3.0, "3.14"],
@@ -42,7 +43,8 @@ def create_test_record
4243
::Time.new(2021, 6, 24, 17, 8, 21, "+02:00"), "2021-06-25 17:08:21 +02:00"],
4344
col_array_json: [{ kind: "user_renamed", change: %w[jack john]}, nil, \
4445
{ kind: "user_renamed", change: %w[alice meredith]},
45-
"{\"kind\":\"user_renamed\",\"change\":[\"bob\",\"carol\"]}"]
46+
"{\"kind\":\"user_renamed\",\"change\":[\"bob\",\"carol\"]}"],
47+
col_array_uuid: ["e986c19c-9bf3-44f8-851d-710ad3d0067b", nil, "a02f50b6-2860-48dc-acd6-49b42abc3094"]
4648
end
4749

4850
def test_create_record
@@ -67,6 +69,7 @@ def test_create_record
6769
assert_equal ::Time.new(2021, 6, 23, 17, 8, 21, "+02:00").utc, record.col_timestamp.utc
6870
assert_equal ({"kind" => "user_renamed", "change" => %w[jack john]}),
6971
record.col_json
72+
assert_equal "3840ba25-55df-4cb7-a210-1fe37278954f", record.col_uuid
7073

7174
assert_equal ["string1", nil, "string2"], record.col_array_string
7275
assert_equal [100, nil, 200, 300], record.col_array_int64
@@ -85,6 +88,7 @@ def test_create_record
8588
{"kind" => "user_renamed", "change" => %w[alice meredith]}, \
8689
"{\"kind\":\"user_renamed\",\"change\":[\"bob\",\"carol\"]}"],
8790
record.col_array_json
91+
assert_equal ["e986c19c-9bf3-44f8-851d-710ad3d0067b", nil, "a02f50b6-2860-48dc-acd6-49b42abc3094"], record.col_array_uuid
8892
end
8993
end
9094

@@ -100,6 +104,7 @@ def test_update_record
100104
col_date: ::Date.new(2021, 6, 28),
101105
col_timestamp: ::Time.new(2021, 6, 28, 11, 22, 21, "+02:00"),
102106
col_json: { kind: "user_created", change: %w[jack alice]},
107+
col_uuid: "b493579f-f4f6-41f6-a520-8373ecf1cde4",
103108
col_array_string: ["new string 1", "new string 2"],
104109
col_array_int64: [300, 200, 100],
105110
col_array_float64: [1.1, 2.2, 3.3],
@@ -108,7 +113,8 @@ def test_update_record
108113
col_array_bytes: [StringIO.new("new bytes 1"), StringIO.new("new bytes 2")],
109114
col_array_date: [::Date.new(2021, 6, 28)],
110115
col_array_timestamp: [::Time.utc(2020, 12, 31, 0, 0, 0)],
111-
col_array_json: [{ kind: "user_created", change: %w[jack alice]}]
116+
col_array_json: [{ kind: "user_created", change: %w[jack alice]}],
117+
col_array_uuid: ["a664b1eb-4dc7-4795-9ed8-28fab6644cea", nil, "1c794d81-bc6d-4771-9c92-a920d28a7aaa"]
112118
end
113119

114120
# Verify that the record was updated.
@@ -123,6 +129,7 @@ def test_update_record
123129
assert_equal ::Time.new(2021, 6, 28, 11, 22, 21, "+02:00").utc, record.col_timestamp.utc
124130
assert_equal ({"kind" => "user_created", "change" => %w[jack alice]}),
125131
record.col_json
132+
assert_equal "b493579f-f4f6-41f6-a520-8373ecf1cde4", record.col_uuid
126133

127134
assert_equal ["new string 1", "new string 2"], record.col_array_string
128135
assert_equal [300, 200, 100], record.col_array_int64
@@ -135,6 +142,8 @@ def test_update_record
135142
assert_equal [::Time.utc(2020, 12, 31, 0, 0, 0)], record.col_array_timestamp.map(&:utc)
136143
assert_equal [{"kind" => "user_created", "change" => %w[jack alice]}],
137144
record.col_array_json
145+
assert_equal ["a664b1eb-4dc7-4795-9ed8-28fab6644cea", nil, "1c794d81-bc6d-4771-9c92-a920d28a7aaa"],
146+
record.col_array_uuid
138147
end
139148
end
140149

@@ -151,7 +160,8 @@ def test_create_empty_arrays
151160
col_array_bytes: [],
152161
col_array_date: [],
153162
col_array_timestamp: [],
154-
col_array_json: []
163+
col_array_json: [],
164+
col_array_uuid: []
155165
end
156166

157167
record = AllTypes.find record.id
@@ -164,6 +174,7 @@ def test_create_empty_arrays
164174
assert_equal [], record.col_array_date
165175
assert_equal [], record.col_array_timestamp
166176
assert_equal [], record.col_array_json
177+
assert_equal [], record.col_array_uuid
167178
end
168179
end
169180
end

acceptance/schema/schema.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ def create_tables_in_test_schema
2424
t.column :col_date, :date
2525
t.column :col_timestamp, :datetime
2626
t.column :col_json, :json
27+
t.column :col_uuid, :uuid
2728

2829
t.column :col_array_string, :string, array: true
2930
t.column :col_array_int64, :bigint, array: true
@@ -34,6 +35,7 @@ def create_tables_in_test_schema
3435
t.column :col_array_date, :date, array: true
3536
t.column :col_array_timestamp, :datetime, array: true
3637
t.column :col_array_json, :json, array: true
38+
t.column :col_array_uuid, :uuid, array: true
3739
end
3840

3941
create_table :firms do |t|

lib/active_record/connection_adapters/spanner/type_mapping.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,6 @@
2121
date: { name: "DATE" },
2222
binary: { name: "BYTES", limit: "MAX" },
2323
boolean: { name: "BOOL" },
24-
json: { name: "JSON" }
24+
json: { name: "JSON" },
25+
uuid: { name: "UUID" }
2526
}.freeze

lib/active_record/connection_adapters/spanner_adapter.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
require "active_record/type/spanner/bytes"
2121
require "active_record/type/spanner/spanner_active_record_converter"
2222
require "active_record/type/spanner/time"
23+
require "active_record/type/spanner/uuid"
2324
require "arel/visitors/spanner"
2425
require "activerecord_spanner_adapter/base"
2526
require "activerecord_spanner_adapter/connection"
@@ -250,6 +251,7 @@ def initialize_type_map m = type_map
250251
register_class_with_limit m, %r{^STRING}i, Type::String
251252
m.register_type "TIMESTAMP", ActiveRecord::Type::Spanner::Time.new
252253
m.register_type "JSON", ActiveRecord::Type::Json.new
254+
m.register_type "UUID", ActiveRecord::Type::Spanner::Uuid.new
253255

254256
register_array_types m
255257
end
@@ -265,6 +267,7 @@ def register_array_types m
265267
m.register_type %r{^ARRAY<STRING\((MAX|d+)\)>}i, Type::Spanner::Array.new(Type::String.new)
266268
m.register_type %r{^ARRAY<TIMESTAMP>}i, Type::Spanner::Array.new(ActiveRecord::Type::Spanner::Time.new)
267269
m.register_type %r{^ARRAY<JSON>}i, Type::Spanner::Array.new(ActiveRecord::Type::Json.new)
270+
m.register_type %r{^ARRAY<UUID>}i, Type::Spanner::Array.new(ActiveRecord::Type::Spanner::Uuid.new)
268271
end
269272

270273
def extract_limit sql_type

lib/active_record/type/spanner/spanner_active_record_converter.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ def self.convert_active_model_type_to_spanner type # rubocop:disable Metrics/Cyc
3737
when ActiveModel::Type::DateTime, ActiveModel::Type::Time, ActiveRecord::Type::Spanner::Time then :TIMESTAMP
3838
when ActiveModel::Type::Date then :DATE
3939
when ActiveRecord::Type::Json then :JSON
40+
when ActiveRecord::Type::Spanner::Uuid then :UUID
4041
when ActiveRecord::Type::Spanner::Array then [convert_active_model_type_to_spanner(type.element_type)]
4142
end
4243
end
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Use of this source code is governed by an MIT-style
4+
# license that can be found in the LICENSE file or at
5+
# https://opensource.org/licenses/MIT.
6+
7+
# frozen_string_literal: true
8+
9+
module ActiveRecord
10+
module Type
11+
module Spanner
12+
class Uuid < ActiveModel::Type::Value
13+
def type
14+
:uuid
15+
end
16+
end
17+
end
18+
end
19+
end

test/activerecord_spanner_mock_server/model_helper.rb

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,17 @@ def self.register_all_types_columns_result spanner_mock_server
537537
Value.new(string_value: "NO"),
538538
)
539539
result_set.rows.push row
540+
row = ListValue.new
541+
row.values.push(
542+
Value.new(string_value: "col_uuid"),
543+
Value.new(string_value: "UUID"),
544+
Value.new(string_value: "YES"),
545+
Value.new(null_value: "NULL_VALUE"),
546+
Value.new(null_value: "NULL_VALUE"),
547+
Value.new(string_value: "11"),
548+
Value.new(string_value: "NO"),
549+
)
550+
result_set.rows.push row
540551

541552
row = ListValue.new
542553
row.values.push(
@@ -545,7 +556,7 @@ def self.register_all_types_columns_result spanner_mock_server
545556
Value.new(string_value: "YES"),
546557
Value.new(null_value: "NULL_VALUE"),
547558
Value.new(null_value: "NULL_VALUE"),
548-
Value.new(string_value: "11"),
559+
Value.new(string_value: "12"),
549560
Value.new(string_value: "NO"),
550561
)
551562
result_set.rows.push row
@@ -556,7 +567,7 @@ def self.register_all_types_columns_result spanner_mock_server
556567
Value.new(string_value: "YES"),
557568
Value.new(null_value: "NULL_VALUE"),
558569
Value.new(null_value: "NULL_VALUE"),
559-
Value.new(string_value: "12"),
570+
Value.new(string_value: "13"),
560571
Value.new(string_value: "NO"),
561572
)
562573
result_set.rows.push row
@@ -567,7 +578,7 @@ def self.register_all_types_columns_result spanner_mock_server
567578
Value.new(string_value: "YES"),
568579
Value.new(null_value: "NULL_VALUE"),
569580
Value.new(null_value: "NULL_VALUE"),
570-
Value.new(string_value: "13"),
581+
Value.new(string_value: "14"),
571582
Value.new(string_value: "NO"),
572583
)
573584
result_set.rows.push row
@@ -578,7 +589,7 @@ def self.register_all_types_columns_result spanner_mock_server
578589
Value.new(string_value: "YES"),
579590
Value.new(null_value: "NULL_VALUE"),
580591
Value.new(null_value: "NULL_VALUE"),
581-
Value.new(string_value: "14"),
592+
Value.new(string_value: "15"),
582593
Value.new(string_value: "NO"),
583594
)
584595
result_set.rows.push row
@@ -589,7 +600,7 @@ def self.register_all_types_columns_result spanner_mock_server
589600
Value.new(string_value: "YES"),
590601
Value.new(null_value: "NULL_VALUE"),
591602
Value.new(null_value: "NULL_VALUE"),
592-
Value.new(string_value: "15"),
603+
Value.new(string_value: "16"),
593604
Value.new(string_value: "NO"),
594605
)
595606
result_set.rows.push row
@@ -600,7 +611,7 @@ def self.register_all_types_columns_result spanner_mock_server
600611
Value.new(string_value: "YES"),
601612
Value.new(null_value: "NULL_VALUE"),
602613
Value.new(null_value: "NULL_VALUE"),
603-
Value.new(string_value: "16"),
614+
Value.new(string_value: "17"),
604615
Value.new(string_value: "NO"),
605616
)
606617
result_set.rows.push row
@@ -611,7 +622,7 @@ def self.register_all_types_columns_result spanner_mock_server
611622
Value.new(string_value: "YES"),
612623
Value.new(null_value: "NULL_VALUE"),
613624
Value.new(null_value: "NULL_VALUE"),
614-
Value.new(string_value: "17"),
625+
Value.new(string_value: "18"),
615626
Value.new(string_value: "NO"),
616627
)
617628
result_set.rows.push row
@@ -622,7 +633,7 @@ def self.register_all_types_columns_result spanner_mock_server
622633
Value.new(string_value: "YES"),
623634
Value.new(null_value: "NULL_VALUE"),
624635
Value.new(null_value: "NULL_VALUE"),
625-
Value.new(string_value: "18"),
636+
Value.new(string_value: "19"),
626637
Value.new(string_value: "NO"),
627638
)
628639
result_set.rows.push row
@@ -633,10 +644,21 @@ def self.register_all_types_columns_result spanner_mock_server
633644
Value.new(string_value: "YES"),
634645
Value.new(null_value: "NULL_VALUE"),
635646
Value.new(null_value: "NULL_VALUE"),
636-
Value.new(string_value: "19"),
647+
Value.new(string_value: "20"),
637648
Value.new(string_value: "NO"),
638649
)
639650
result_set.rows.push row
651+
row = ListValue.new
652+
row.values.push(
653+
Value.new(string_value: "col_array_uuid"),
654+
Value.new(string_value: "ARRAY<UUID>"),
655+
Value.new(string_value: "YES"),
656+
Value.new(null_value: "NULL_VALUE"),
657+
Value.new(null_value: "NULL_VALUE"),
658+
Value.new(string_value: "21"),
659+
Value.new(string_value: "NO"),
660+
)
661+
result_set.rows.push row
640662

641663
spanner_mock_server.put_statement_result sql, StatementResult.new(result_set)
642664
end

0 commit comments

Comments
 (0)