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  }