Skip to content

Commit b96ad48

Browse files
sql/ydb: fixed integration errors (#3)
1 parent fa87bfd commit b96ad48

File tree

13 files changed

+640
-186
lines changed

13 files changed

+640
-186
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ require (
1010
github.com/mattn/go-sqlite3 v1.14.28
1111
github.com/prometheus/common v0.66.1
1212
github.com/stretchr/testify v1.11.1
13-
github.com/ydb-platform/ydb-go-sdk/v3 v3.125.0
13+
github.com/ydb-platform/ydb-go-sdk/v3 v3.125.4
1414
github.com/zclconf/go-cty v1.14.4
1515
github.com/zclconf/go-cty-yaml v1.1.0
1616
golang.org/x/mod v0.30.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,8 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
107107
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
108108
github.com/ydb-platform/ydb-go-genproto v0.0.0-20251222105147-0bf751469a4a h1:nRqONRrMFulP2bTWM2RRnPM1VDhWuBZg4ULXkG4xXdk=
109109
github.com/ydb-platform/ydb-go-genproto v0.0.0-20251222105147-0bf751469a4a/go.mod h1:Er+FePu1dNUieD+XTMDduGpQuCPssK5Q4BjF+IIXJ3I=
110-
github.com/ydb-platform/ydb-go-sdk/v3 v3.125.0 h1:KPnGV2diuX1A4/1zXLO1UWHJokWC8yICzEfjdkSUWKo=
111-
github.com/ydb-platform/ydb-go-sdk/v3 v3.125.0/go.mod h1:/LjMxb/rXmoGAAnImoqAFIlhO5ampHacbvDetQitCk4=
110+
github.com/ydb-platform/ydb-go-sdk/v3 v3.125.4 h1:GAC7qeNgsibEJkUVzV4z06aBnHR4jqfXsFiQtrY40gI=
111+
github.com/ydb-platform/ydb-go-sdk/v3 v3.125.4/go.mod h1:stS1mQYjbJvwwYaYzKyFY9eMiuVXWWXQA6T+SpOLg9c=
112112
github.com/zclconf/go-cty v1.14.4 h1:uXXczd9QDGsgu0i/QFR/hzI5NYCHLf6NQw/atrbnhq8=
113113
github.com/zclconf/go-cty v1.14.4/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
114114
github.com/zclconf/go-cty-yaml v1.1.0 h1:nP+jp0qPHv2IhUVqmQSzjvqAWcObN0KBkUl2rWBdig0=

sql/ydb/attributes.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ package ydb
88

99
import "ariga.io/atlas/sql/schema"
1010

11-
//[IndexAttributes] represents YDB-specific index attributes.
11+
// [IndexAttributes] represents YDB-specific index attributes.
1212
type IndexAttributes struct {
1313
schema.Attr
14-
Global bool // GLOBAL, LOCAL
15-
Sync bool // SYNC, ASYNC
14+
Async bool
15+
CoverColumns []*schema.Column
1616
}

sql/ydb/convert.go

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func FormatType(typ schema.Type) (string, error) {
2323
)
2424

2525
switch t := typ.(type) {
26-
case OptionalType:
26+
case *OptionalType:
2727
formatted = t.T
2828
case *schema.BoolType:
2929
formatted = TypeBool
@@ -41,7 +41,7 @@ func FormatType(typ schema.Type) (string, error) {
4141
formatted = TypeUtf8
4242
case *schema.JSONType:
4343
formatted, err = formatJSONType(t)
44-
case YsonType:
44+
case *YsonType:
4545
formatted = t.T
4646
case *schema.UUIDType:
4747
formatted = TypeUUID
@@ -61,69 +61,67 @@ func FormatType(typ schema.Type) (string, error) {
6161
return formatted, nil
6262
}
6363

64-
func formatIntegerType(t *schema.IntegerType) (string, error) {
65-
typ := strings.ToLower(t.T)
66-
switch typ {
64+
func formatIntegerType(intType *schema.IntegerType) (string, error) {
65+
switch typ := strings.ToLower(intType.T); typ {
6766
case TypeInt8:
68-
if t.Unsigned {
67+
if intType.Unsigned {
6968
return TypeUint8, nil
7069
}
7170
return TypeInt8, nil
7271
case TypeInt16:
73-
if t.Unsigned {
72+
if intType.Unsigned {
7473
return TypeUint16, nil
7574
}
7675
return TypeInt16, nil
7776
case TypeInt32:
78-
if t.Unsigned {
77+
if intType.Unsigned {
7978
return TypeUint32, nil
8079
}
8180
return TypeInt32, nil
8281
case TypeInt64:
83-
if t.Unsigned {
82+
if intType.Unsigned {
8483
return TypeUint64, nil
8584
}
8685
return TypeInt64, nil
8786
case TypeUint8, TypeUint16, TypeUint32, TypeUint64:
8887
return typ, nil
8988
default:
90-
return "", fmt.Errorf("ydb: unsupported object identifier type: %q", t.T)
89+
return "", fmt.Errorf("ydb: unsupported object identifier type: %q", intType.T)
9190
}
9291
}
9392

94-
func formatFloatType(t *schema.FloatType) (string, error) {
95-
typ := strings.ToLower(t.T)
96-
switch typ {
93+
func formatFloatType(floatType *schema.FloatType) (string, error) {
94+
switch typ := strings.ToLower(floatType.T); typ {
9795
case TypeFloat, TypeDouble:
9896
return typ, nil
9997
default:
100-
return "", fmt.Errorf("ydb: unsupported object identifier type: %q", t.T)
98+
return "", fmt.Errorf("ydb: unsupported object identifier type: %q", floatType.T)
10199
}
102100
}
103101

104-
func formatDecimalType(t *schema.DecimalType) (string, error) {
105-
if t.Precision < 1 || t.Precision > 35 {
106-
return "", fmt.Errorf("ydb: DECIMAL precision must be in [1, 35] range, but was %q", t.Precision)
102+
func formatDecimalType(decType *schema.DecimalType) (string, error) {
103+
if decType.Precision < 1 || decType.Precision > 35 {
104+
return "", fmt.Errorf("ydb: DECIMAL precision must be in [1, 35] range, but was %q", decType.Precision)
107105
}
108-
if t.Scale < 0 || t.Scale > t.Precision {
109-
return "", fmt.Errorf("ydb: DECIMAL scale must be in [1, precision] range, but was %q", t.Precision)
106+
if decType.Scale < 0 || decType.Scale > decType.Precision {
107+
return "", fmt.Errorf("ydb: DECIMAL scale must be in [1, precision] range, but was %q", decType.Precision)
110108
}
111109

112-
return fmt.Sprintf("%s(%d,%d)", TypeDecimal, t.Precision, t.Scale), nil
110+
return fmt.Sprintf("%s(%d,%d)", TypeDecimal, decType.Precision, decType.Scale), nil
113111
}
114112

115-
func formatJSONType(t *schema.JSONType) (string, error) {
116-
typ := strings.ToLower(t.T)
113+
func formatJSONType(jsonType *schema.JSONType) (string, error) {
114+
typ := strings.ToLower(jsonType.T)
117115
switch typ {
118116
case TypeJSONDocument, TypeJSON:
119117
return typ, nil
120118
default:
121-
return "", fmt.Errorf("ydb: unsupported object identifier type: %q", t.T)
119+
return "", fmt.Errorf("ydb: unsupported object identifier type: %q", jsonType.T)
122120
}
123121
}
124122

125-
func formatTimeType(t *schema.TimeType) (string, error) {
126-
switch typ := strings.ToLower(t.T); typ {
123+
func formatTimeType(timeType *schema.TimeType) (string, error) {
124+
switch typ := strings.ToLower(timeType.T); typ {
127125
case TypeDate,
128126
TypeDate32,
129127
TypeDateTime,
@@ -140,7 +138,7 @@ func formatTimeType(t *schema.TimeType) (string, error) {
140138
TypeTzTimestamp64:
141139
return typ, nil
142140
default:
143-
return "", fmt.Errorf("ydb: unsupported object identifier type: %q", t.T)
141+
return "", fmt.Errorf("ydb: unsupported object identifier type: %q", timeType.T)
144142
}
145143
}
146144

sql/ydb/convert_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func TestConvert_FormatType(t *testing.T) {
6969
{name: "jsondocument", typ: &schema.JSONType{T: TypeJSONDocument}, expected: TypeJSONDocument},
7070

7171
// YSON type
72-
{name: "yson", typ: YsonType{T: TypeYson}, expected: TypeYson},
72+
{name: "yson", typ: &YsonType{T: TypeYson}, expected: TypeYson},
7373

7474
// UUID type
7575
{name: "uuid", typ: &schema.UUIDType{T: TypeUUID}, expected: TypeUUID},
@@ -93,7 +93,7 @@ func TestConvert_FormatType(t *testing.T) {
9393
{name: "tztimestamp64", typ: &schema.TimeType{T: TypeTzTimestamp64}, expected: TypeTzTimestamp64},
9494

9595
// Optional type
96-
{name: "optional_int32", typ: OptionalType{T: "Optional<int32>", InnerType: &schema.IntegerType{T: TypeInt32}}, expected: "Optional<int32>"},
96+
{name: "optional_int32", typ: &OptionalType{T: "Optional<int32>", InnerType: &schema.IntegerType{T: TypeInt32}}, expected: "Optional<int32>"},
9797

9898
// Error cases
9999
{name: "unsupported_type", typ: &schema.UnsupportedType{T: "unknown"}, wantErr: true},

sql/ydb/diff.go

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ import (
1717
// DefaultDiff provides basic diffing capabilities for YDB dialect.
1818
// Note, it is recommended to call Open, create a new Driver and use its
1919
// Differ when a database connection is available.
20-
var DefaultDiff schema.Differ = &sqlx.Diff{DiffDriver: &diff{&conn{ExecQuerier: sqlx.NoRows}}}
20+
var DefaultDiff schema.Differ = &sqlx.Diff{
21+
DiffDriver: &diff{&conn{ExecQuerier: sqlx.NoRows}},
22+
}
2123

2224
// A diff provides a YDB implementation for sqlx.DiffDriver.
2325
type diff struct {
@@ -116,7 +118,7 @@ func (d *diff) typeChanged(from *schema.Column, to *schema.Column) (bool, error)
116118
}
117119

118120
// defaultChanged reports if the default value of a column was changed.
119-
func (d *diff) defaultChanged(from, to *schema.Column) bool {
121+
func (d *diff) defaultChanged(from *schema.Column, to *schema.Column) bool {
120122
default1, ok1 := sqlx.DefaultValue(from)
121123
default2, ok2 := sqlx.DefaultValue(to)
122124
if ok1 != ok2 {
@@ -126,13 +128,31 @@ func (d *diff) defaultChanged(from, to *schema.Column) bool {
126128
}
127129

128130
// IndexAttrChanged reports if the index attributes were changed.
129-
func (*diff) IndexAttrChanged(_, _ []schema.Attr) bool {
130-
return false // unimplemented.
131+
func (*diff) IndexAttrChanged(from, to []schema.Attr) bool {
132+
var fromAttrs, toAttrs IndexAttributes
133+
sqlx.Has(from, &fromAttrs)
134+
sqlx.Has(to, &toAttrs)
135+
136+
if fromAttrs.Async != toAttrs.Async {
137+
return true
138+
}
139+
140+
if len(fromAttrs.CoverColumns) != len(toAttrs.CoverColumns) {
141+
return true
142+
}
143+
144+
for i := range fromAttrs.CoverColumns {
145+
if fromAttrs.CoverColumns[i].Name != toAttrs.CoverColumns[i].Name {
146+
return true
147+
}
148+
}
149+
return false
131150
}
132151

133152
// IndexPartAttrChanged reports if the index-part attributes were changed.
134153
func (*diff) IndexPartAttrChanged(_, _ *schema.Index, _ int) bool {
135-
return false // unimplemented.
154+
// YDB doesn't have per-part attributes like collation, prefix, or operator classes.
155+
return false
136156
}
137157

138158
// IsGeneratedIndexName reports if the index name was generated by the database.

sql/ydb/diff_test.go

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,70 @@ func TestDiff_TableDiff(t *testing.T) {
241241
},
242242
}
243243
}(),
244+
func() testcase {
245+
var (
246+
from = &schema.Table{
247+
Name: "users",
248+
Schema: &schema.Schema{Name: "local"},
249+
Columns: []*schema.Column{
250+
{Name: "id", Type: &schema.ColumnType{Type: &schema.IntegerType{T: TypeInt32}}},
251+
},
252+
}
253+
to = &schema.Table{
254+
Name: "users",
255+
Columns: []*schema.Column{
256+
{Name: "id", Type: &schema.ColumnType{Type: &schema.IntegerType{T: TypeInt32}}},
257+
},
258+
}
259+
)
260+
from.Indexes = []*schema.Index{
261+
{Name: "idx_id", Table: from, Parts: []*schema.IndexPart{{SeqNo: 1, C: from.Columns[0]}}, Attrs: []schema.Attr{&IndexAttributes{Async: false}}},
262+
}
263+
to.Indexes = []*schema.Index{
264+
{Name: "idx_id", Table: to, Parts: []*schema.IndexPart{{SeqNo: 1, C: to.Columns[0]}}, Attrs: []schema.Attr{&IndexAttributes{Async: true}}},
265+
}
266+
return testcase{
267+
name: "modify index async",
268+
from: from,
269+
to: to,
270+
wantChanges: []schema.Change{
271+
&schema.ModifyIndex{From: from.Indexes[0], To: to.Indexes[0], Change: schema.ChangeAttr},
272+
},
273+
}
274+
}(),
275+
func() testcase {
276+
var (
277+
from = &schema.Table{
278+
Name: "users",
279+
Schema: &schema.Schema{Name: "local"},
280+
Columns: []*schema.Column{
281+
{Name: "id", Type: &schema.ColumnType{Type: &schema.IntegerType{T: TypeInt32}}},
282+
{Name: "name", Type: &schema.ColumnType{Type: &schema.StringType{T: TypeUtf8}}},
283+
},
284+
}
285+
to = &schema.Table{
286+
Name: "users",
287+
Columns: []*schema.Column{
288+
{Name: "id", Type: &schema.ColumnType{Type: &schema.IntegerType{T: TypeInt32}}},
289+
{Name: "name", Type: &schema.ColumnType{Type: &schema.StringType{T: TypeUtf8}}},
290+
},
291+
}
292+
)
293+
from.Indexes = []*schema.Index{
294+
{Name: "idx_id", Table: from, Parts: []*schema.IndexPart{{SeqNo: 1, C: from.Columns[0]}}},
295+
}
296+
to.Indexes = []*schema.Index{
297+
{Name: "idx_id", Table: to, Parts: []*schema.IndexPart{{SeqNo: 1, C: to.Columns[0]}}, Attrs: []schema.Attr{&IndexAttributes{CoverColumns: []*schema.Column{to.Columns[1]}}}},
298+
}
299+
return testcase{
300+
name: "modify index add cover columns",
301+
from: from,
302+
to: to,
303+
wantChanges: []schema.Change{
304+
&schema.ModifyIndex{From: from.Indexes[0], To: to.Indexes[0], Change: schema.ChangeAttr},
305+
},
306+
}
307+
}(),
244308
}
245309

246310
for _, tt := range tests {
@@ -406,3 +470,104 @@ func TestDiff_DefaultChanged(t *testing.T) {
406470
})
407471
}
408472
}
473+
474+
func TestDiff_IndexAttrChanged(t *testing.T) {
475+
d := &diff{conn: &conn{}}
476+
477+
col1 := &schema.Column{Name: "name"}
478+
col2 := &schema.Column{Name: "email"}
479+
480+
tests := []struct {
481+
name string
482+
from []schema.Attr
483+
to []schema.Attr
484+
changed bool
485+
}{
486+
{
487+
name: "no attributes",
488+
from: nil,
489+
to: nil,
490+
},
491+
{
492+
name: "add async attribute",
493+
from: nil,
494+
to: []schema.Attr{&IndexAttributes{Async: true}},
495+
changed: true,
496+
},
497+
{
498+
name: "remove async attribute",
499+
from: []schema.Attr{&IndexAttributes{Async: true}},
500+
to: nil,
501+
changed: true,
502+
},
503+
{
504+
name: "same async false",
505+
from: []schema.Attr{&IndexAttributes{Async: false}},
506+
to: []schema.Attr{&IndexAttributes{Async: false}},
507+
},
508+
{
509+
name: "same async true",
510+
from: []schema.Attr{&IndexAttributes{Async: true}},
511+
to: []schema.Attr{&IndexAttributes{Async: true}},
512+
},
513+
{
514+
name: "change async false to true",
515+
from: []schema.Attr{&IndexAttributes{Async: false}},
516+
to: []schema.Attr{&IndexAttributes{Async: true}},
517+
changed: true,
518+
},
519+
{
520+
name: "change async true to false",
521+
from: []schema.Attr{&IndexAttributes{Async: true}},
522+
to: []schema.Attr{&IndexAttributes{Async: false}},
523+
changed: true,
524+
},
525+
{
526+
name: "same cover columns",
527+
from: []schema.Attr{&IndexAttributes{CoverColumns: []*schema.Column{col1}}},
528+
to: []schema.Attr{&IndexAttributes{CoverColumns: []*schema.Column{col1}}},
529+
},
530+
{
531+
name: "add cover columns",
532+
from: []schema.Attr{&IndexAttributes{}},
533+
to: []schema.Attr{&IndexAttributes{CoverColumns: []*schema.Column{col1}}},
534+
changed: true,
535+
},
536+
{
537+
name: "remove cover columns",
538+
from: []schema.Attr{&IndexAttributes{CoverColumns: []*schema.Column{col1}}},
539+
to: []schema.Attr{&IndexAttributes{}},
540+
changed: true,
541+
},
542+
{
543+
name: "different cover columns count",
544+
from: []schema.Attr{&IndexAttributes{CoverColumns: []*schema.Column{col1}}},
545+
to: []schema.Attr{&IndexAttributes{CoverColumns: []*schema.Column{col1, col2}}},
546+
changed: true,
547+
},
548+
{
549+
name: "different cover column names",
550+
from: []schema.Attr{&IndexAttributes{CoverColumns: []*schema.Column{col1}}},
551+
to: []schema.Attr{&IndexAttributes{CoverColumns: []*schema.Column{col2}}},
552+
changed: true,
553+
},
554+
{
555+
name: "same async and cover columns",
556+
from: []schema.Attr{&IndexAttributes{Async: true, CoverColumns: []*schema.Column{col1, col2}}},
557+
to: []schema.Attr{&IndexAttributes{Async: true, CoverColumns: []*schema.Column{col1, col2}}},
558+
},
559+
{
560+
name: "same cover columns different async",
561+
from: []schema.Attr{&IndexAttributes{Async: false, CoverColumns: []*schema.Column{col1}}},
562+
to: []schema.Attr{&IndexAttributes{Async: true, CoverColumns: []*schema.Column{col1}}},
563+
changed: true,
564+
},
565+
}
566+
567+
for _, tt := range tests {
568+
t.Run(tt.name, func(t *testing.T) {
569+
changed := d.IndexAttrChanged(tt.from, tt.to)
570+
require.Equal(t, tt.changed, changed)
571+
})
572+
}
573+
}

0 commit comments

Comments
 (0)