github.com/ydb-platform/ydb-go-sdk/v3@v3.57.0/tests/integration/database_sql_containers_test.go (about) 1 //go:build integration 2 // +build integration 3 4 package integration 5 6 import ( 7 "context" 8 "database/sql" 9 "fmt" 10 "os" 11 "testing" 12 13 "github.com/stretchr/testify/require" 14 15 "github.com/ydb-platform/ydb-go-sdk/v3" 16 "github.com/ydb-platform/ydb-go-sdk/v3/retry" 17 "github.com/ydb-platform/ydb-go-sdk/v3/table/types" 18 ) 19 20 // https://github.com/ydb-platform/ydb-go-sdk/issues/757 21 // Containers example demonstrates how to work with YDB container values such as `List`, `Tuple`, `Dict`, `Struct` and `Variant` 22 func TestDatabaseSqlContainers(t *testing.T) { 23 ctx, cancel := context.WithCancel(context.Background()) 24 defer cancel() 25 26 nativeDriver, err := ydb.Open(ctx, 27 os.Getenv("YDB_CONNECTION_STRING"), 28 ydb.WithAccessTokenCredentials(os.Getenv("YDB_ACCESS_TOKEN_CREDENTIALS")), 29 ) 30 require.NoError(t, err) 31 defer func() { 32 _ = nativeDriver.Close(ctx) 33 }() 34 35 connector, err := ydb.Connector(nativeDriver) 36 require.NoError(t, err) 37 defer func() { 38 _ = connector.Close() 39 }() 40 41 db := sql.OpenDB(connector) 42 43 err = retry.Do(ctx, db, func(ctx context.Context, cc *sql.Conn) error { 44 rows, err := cc.QueryContext(ctx, ` 45 SELECT 46 AsList("foo", "bar", "baz"); 47 SELECT 48 AsTuple(42, "foo", AsList(41, 42, 43)); 49 SELECT 50 AsDict( 51 AsTuple("foo"u, 10), 52 AsTuple("bar"u, 20), 53 AsTuple("baz"u, 30), 54 ); 55 SELECT 56 AsStruct( 57 41 AS foo, 58 42 AS bar, 59 43 AS baz, 60 ); 61 62 $struct = AsStruct( 63 Uint32("0") as foo, 64 UTF8("x") as bar, 65 Int64("0") as baz, 66 ); 67 $variantStructType = VariantType(TypeOf($struct)); 68 SELECT Variant(42, "baz", $variantStructType); 69 70 $tuple = AsTuple( 71 Uint32("0"), 72 UTF8("x"), 73 Int64("0"), 74 ); 75 $variantTupleType = VariantType(TypeOf($tuple)); 76 SELECT Variant(42, "2", $variantTupleType); 77 SELECT CAST(42 AS Int64); 78 `) 79 if err != nil { 80 return err 81 } 82 defer func() { 83 _ = rows.Close() 84 }() 85 86 parsers := [...]func() error{ 87 func() error { 88 return rows.Scan(&testDatabaseSqlContainersExampleList{}) 89 }, 90 func() error { 91 return rows.Scan(&testDatabaseSqlContainersExampleTuple{}) 92 }, 93 func() error { 94 return rows.Scan(&testDatabaseSqlContainersExampleDict{}) 95 }, 96 func() error { 97 return rows.Scan(&testDatabaseSqlContainersExampleStruct{}) 98 }, 99 func() error { 100 return rows.Scan(&testDatabaseSqlContainersVariantStruct{}) 101 }, 102 func() error { 103 return rows.Scan(&testDatabaseSqlContainersVariantTuple{test: t}) 104 }, 105 func() error { 106 var v int64 107 return rows.Scan(&v) 108 }, 109 } 110 111 for set := 0; rows.NextResultSet(); set++ { 112 rows.Next() 113 err = parsers[set]() 114 if err != nil { 115 return err 116 } 117 } 118 return rows.Err() 119 }, retry.WithIdempotent(true)) 120 require.NoError(t, err) 121 } 122 123 type testDatabaseSqlContainersExampleStruct struct { 124 foo int 125 bar int 126 baz int 127 } 128 129 func (s *testDatabaseSqlContainersExampleStruct) Scan(res interface{}) error { 130 if v, has := res.(types.Value); has { 131 if fields, err := types.StructFields(v); err != nil { 132 return err 133 } else { 134 if v, ok := fields["foo"]; !ok { 135 return fmt.Errorf("struct '%v' (type '%s') cannot have 'foo' field", v, v.Type().Yql()) 136 } else if err := types.CastTo(v, &s.foo); err != nil { 137 return err 138 } 139 if v, ok := fields["bar"]; !ok { 140 return fmt.Errorf("struct '%v' (type '%s') cannot have 'bar' field", v, v.Type().Yql()) 141 } else if err := types.CastTo(v, &s.bar); err != nil { 142 return err 143 } 144 if v, ok := fields["baz"]; !ok { 145 return fmt.Errorf("struct '%v' (type '%s') cannot have 'baz' field", v, v.Type().Yql()) 146 } else if err := types.CastTo(v, &s.baz); err != nil { 147 return err 148 } 149 return nil 150 } 151 } 152 return fmt.Errorf("type '%T' is not a `types.value` type", res) 153 } 154 155 type testDatabaseSqlContainersExampleList []string 156 157 func (list *testDatabaseSqlContainersExampleList) Scan(res interface{}) error { 158 if v, has := res.(types.Value); has { 159 if items, err := types.ListItems(v); err != nil { 160 return err 161 } else { 162 *list = make([]string, len(items)) 163 for i, item := range items { 164 var v string 165 if err := types.CastTo(item, &v); err != nil { 166 return err 167 } 168 (*list)[i] = v 169 } 170 return nil 171 } 172 } 173 return fmt.Errorf("type '%T' is not a `types.Value` type", res) 174 } 175 176 type testDatabaseSqlContainersExampleTuple struct { 177 a int 178 b string 179 c []int 180 } 181 182 func (tuple *testDatabaseSqlContainersExampleTuple) Scan(res interface{}) error { 183 if v, has := res.(types.Value); has { 184 if items, err := types.TupleItems(v); err != nil { 185 return err 186 } else { 187 if len(items) != 3 { 188 return fmt.Errorf("tuple items not consists with testDatabaseSqlContainersExampleTuple type") 189 } 190 if err := types.CastTo(items[0], &tuple.a); err != nil { 191 return err 192 } 193 if err := types.CastTo(items[1], &tuple.b); err != nil { 194 return err 195 } 196 if items, err := types.ListItems(items[2]); err != nil { 197 return err 198 } else { 199 tuple.c = make([]int, len(items)) 200 for i, item := range items { 201 var v int 202 if err := types.CastTo(item, &v); err != nil { 203 return err 204 } 205 tuple.c[i] = i 206 } 207 return nil 208 } 209 } 210 } 211 return fmt.Errorf("type '%T' is not a `types.Value` type", res) 212 } 213 214 type testDatabaseSqlContainersExampleDict map[string]int 215 216 func (dict *testDatabaseSqlContainersExampleDict) Scan(res interface{}) error { 217 if v, has := res.(types.Value); has { 218 if items, err := types.DictValues(v); err != nil { 219 return err 220 } else { 221 *dict = make(map[string]int, len(items)) 222 for k, v := range items { 223 var ( 224 key string 225 value int 226 ) 227 if err := types.CastTo(k, &key); err != nil { 228 return err 229 } 230 if err := types.CastTo(v, &value); err != nil { 231 return err 232 } 233 (*dict)[key] = value 234 } 235 return nil 236 } 237 } 238 return fmt.Errorf("type '%T' is not a `types.Value` type", res) 239 } 240 241 type testDatabaseSqlContainersVariantStruct struct { 242 foo uint32 243 bar string 244 baz int64 245 } 246 247 func (s *testDatabaseSqlContainersVariantStruct) Scan(res interface{}) error { 248 if v, has := res.(types.Value); has { 249 if name, _, value, err := types.VariantValue(v); err != nil { 250 return err 251 } else { 252 switch name { 253 case "foo": 254 if err := types.CastTo(value, &s.foo); err != nil { 255 return err 256 } 257 case "bar": 258 if err := types.CastTo(value, &s.bar); err != nil { 259 return err 260 } 261 case "baz": 262 if err := types.CastTo(value, &s.baz); err != nil { 263 return err 264 } 265 default: 266 return fmt.Errorf("unexpected variant struct field name '%s'", name) 267 } 268 return nil 269 } 270 } 271 return fmt.Errorf("type '%T' is not a `types.Value` type", res) 272 } 273 274 type testDatabaseSqlContainersVariantTuple struct { 275 a uint32 276 b string 277 c int64 278 test testing.TB 279 } 280 281 func (s *testDatabaseSqlContainersVariantTuple) Scan(res interface{}) error { 282 if v, has := res.(types.Value); has { 283 if _, idx, value, err := types.VariantValue(v); err != nil { 284 return err 285 } else { 286 switch idx { 287 case 0: 288 if err := types.CastTo(value, &s.a); err != nil { 289 return err 290 } 291 case 1: 292 if err := types.CastTo(value, &s.b); err != nil { 293 return err 294 } 295 case 2: 296 if err := types.CastTo(value, &s.c); err != nil { 297 return err 298 } 299 default: 300 return fmt.Errorf("unexpected tuple field index '%d'", idx) 301 } 302 return nil 303 } 304 } 305 return fmt.Errorf("type '%T' is not a `types.Value` type", res) 306 } 307 308 func (s *testDatabaseSqlContainersVariantTuple) UnmarshalYDB(res types.RawValue) error { 309 s.test.Logf("T: %s", res.Type()) 310 name, index := res.Variant() 311 var x interface{} 312 switch index { 313 case 0: 314 x = res.Uint32() 315 case 1: 316 x = res.UTF8() 317 case 2: 318 x = res.Int64() 319 } 320 s.test.Logf( 321 "(tuple variant): %s %s %q %d = %v", 322 res.Path(), res.Type(), name, index, x, 323 ) 324 return res.Err() 325 }