public async Task <List <int> > QueryDataWithStructAsync(string projectId, string instanceId, string databaseId) { // [START spanner_create_struct_with_data] var nameStruct = new SpannerStruct { { "FirstName", SpannerDbType.String, "Elena" }, { "LastName", SpannerDbType.String, "Campbell" }, }; // [END spanner_create_struct_with_data] string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}"; var singerIds = new List <int>(); using var connection = new SpannerConnection(connectionString); using var cmd = connection.CreateSelectCommand( "SELECT SingerId FROM Singers " + "WHERE STRUCT<FirstName STRING, LastName STRING>" + "(FirstName, LastName) = @name"); cmd.Parameters.Add("name", nameStruct.GetSpannerDbType(), nameStruct); using var reader = await cmd.ExecuteReaderAsync(); while (await reader.ReadAsync()) { singerIds.Add(reader.GetFieldValue <int>("SingerId")); } return(singerIds); }
public void NullProtection() { Assert.Throws <ArgumentNullException>(() => new SpannerStruct.Field("name", null, "value")); var value = new SpannerStruct(); Assert.Throws <ArgumentNullException>(() => value.Add("name", null, "value")); Assert.Throws <ArgumentNullException>(() => value.Add(null)); }
public void NullNameCoalescing() { var value = new SpannerStruct { { null, SpannerDbType.String, "foo" }, new SpannerStruct.Field(null, SpannerDbType.String, "bar") }; Assert.Equal("", value[0].Name); Assert.Equal("", value[1].Name); }
private static void AssertSampleStruct(SpannerStruct actual) { Assert.Equal(s_sampleStruct.Count, actual.Count); for (int i = 0; i < s_sampleStruct.Count; i++) { var expectedField = s_sampleStruct[i]; var actualField = actual[i]; Assert.Equal(expectedField.Name, actualField.Name); Assert.Equal(expectedField.Type, actualField.Type); Assert.Equal(expectedField.Value, actualField.Value); } }
public void SimpleProperties() { var value = new SpannerStruct { { "f", SpannerDbType.String, "value" } }; var field = value[0]; Assert.Equal("f", field.Name); Assert.Equal(SpannerDbType.String, field.Type); Assert.Equal("value", field.Value); }
public void InterfaceAccess() { var value = new SpannerStruct { { "f", SpannerDbType.String, null }, { "g", SpannerDbType.Int64, 100L }, }; var fields = value.ToList(); Assert.Same(fields[0], value[0]); Assert.Same(fields[1], value[1]); Assert.Equal(2, fields.Count); Assert.Throws <ArgumentOutOfRangeException>(() => fields[2]); }
public async Task <int> UpdateUsingDmlWithStructCoreAsync(string projectId, string instanceId, string databaseId) { var nameStruct = new SpannerStruct { { "FirstName", SpannerDbType.String, "Timothy" }, { "LastName", SpannerDbType.String, "Campbell" } }; string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}"; using var connection = new SpannerConnection(connectionString); await connection.OpenAsync(); using var cmd = connection.CreateDmlCommand("UPDATE Singers SET LastName = 'Grant' WHERE STRUCT<FirstName STRING, LastName STRING>(FirstName, LastName) = @name"); cmd.Parameters.Add("name", nameStruct.GetSpannerDbType(), nameStruct); int rowCount = await cmd.ExecuteNonQueryAsync(); Console.WriteLine($"{rowCount} row(s) updated..."); return(rowCount); }
public void DeserializeComplexStruct() { var complexStruct = new SpannerStruct { { "StructField", s_sampleStruct.GetSpannerDbType(), s_sampleStruct }, { "ArrayField", SpannerDbType.ArrayOf(SpannerDbType.Int64), GetIntsForArray().Select(x => (long)x).ToList() } }; var wireValue = JsonParser.Default.Parse <Value>($"[ {s_sampleStructSerialized}, [ \"4\", \"5\", \"6\" ] ]"); var actual = complexStruct.GetSpannerDbType().ConvertToClrType <SpannerStruct>(wireValue, SpannerConversionOptions.Default); Assert.Equal(2, actual.Count); Assert.Equal("StructField", actual[0].Name); Assert.Equal(s_sampleStruct.GetSpannerDbType(), actual[0].Type); AssertSampleStruct((SpannerStruct)actual[0].Value); Assert.Equal("ArrayField", actual[1].Name); Assert.Equal(SpannerDbType.ArrayOf(SpannerDbType.Int64), actual[1].Type); Assert.Equal(new[] { 4L, 5L, 6L }, actual[1].Value); }
public async Task <List <int> > QueryDataWithNestedStructFieldAsync(string projectId, string instanceId, string databaseId) { string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}"; SpannerStruct name1 = new SpannerStruct { { "FirstName", SpannerDbType.String, "Elena" }, { "LastName", SpannerDbType.String, "Campbell" } }; SpannerStruct name2 = new SpannerStruct { { "FirstName", SpannerDbType.String, "Hannah" }, { "LastName", SpannerDbType.String, "Harris" } }; SpannerStruct songInfo = new SpannerStruct { { "song_name", SpannerDbType.String, "Imagination" }, { "artistNames", SpannerDbType.ArrayOf(name1.GetSpannerDbType()), new[] { name1, name2 } } }; var singerIds = new List <int>(); using var connection = new SpannerConnection(connectionString); using var cmd = connection.CreateSelectCommand( "SELECT SingerId, @song_info.song_name " + "FROM Singers WHERE STRUCT<FirstName STRING, LastName STRING>(FirstName, LastName) " + "IN UNNEST(@song_info.artistNames)"); cmd.Parameters.Add("song_info", songInfo.GetSpannerDbType(), songInfo); using var reader = await cmd.ExecuteReaderAsync(); while (await reader.ReadAsync()) { var singerId = reader.GetFieldValue <int>("SingerId"); singerIds.Add(singerId); Console.WriteLine($"SingerId: {singerId}"); Console.WriteLine($"Song Name: {reader.GetFieldValue<string>(1)}"); } return(singerIds); }
public async Task <List <int> > QueryDataWithArrayOfStructAsync(string projectId, string instanceId, string databaseId) { // [START spanner_create_user_defined_struct] var nameType = new SpannerStruct { { "FirstName", SpannerDbType.String, null }, { "LastName", SpannerDbType.String, null } }; // [END spanner_create_user_defined_struct] // [START spanner_create_array_of_struct_with_data] var bandMembers = new List <SpannerStruct> { new SpannerStruct { { "FirstName", SpannerDbType.String, "Elena" }, { "LastName", SpannerDbType.String, "Campbell" } }, new SpannerStruct { { "FirstName", SpannerDbType.String, "Gabriel" }, { "LastName", SpannerDbType.String, "Wright" } }, new SpannerStruct { { "FirstName", SpannerDbType.String, "Benjamin" }, { "LastName", SpannerDbType.String, "Martinez" } }, }; // [END spanner_create_array_of_struct_with_data] var singerIds = new List <int>(); string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}"; using var connection = new SpannerConnection(connectionString); using var cmd = connection.CreateSelectCommand( "SELECT SingerId FROM Singers WHERE STRUCT<FirstName STRING, LastName STRING> " + "(FirstName, LastName) IN UNNEST(@names)"); cmd.Parameters.Add("names", SpannerDbType.ArrayOf(nameType.GetSpannerDbType()), bandMembers); using var reader = await cmd.ExecuteReaderAsync(); while (await reader.ReadAsync()) { singerIds.Add(reader.GetFieldValue <int>("SingerId")); } return(singerIds); }
public async Task <List <int> > QueryDataWithStructFieldAsync(string projectId, string instanceId, string databaseId) { string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}"; var structParam = new SpannerStruct { { "FirstName", SpannerDbType.String, "Elena" }, { "LastName", SpannerDbType.String, "Campbell" }, }; var singerIds = new List <int>(); using var connection = new SpannerConnection(connectionString); using var cmd = connection.CreateSelectCommand("SELECT SingerId FROM Singers WHERE FirstName = @name.FirstName"); cmd.Parameters.Add("name", structParam.GetSpannerDbType(), structParam); using var reader = await cmd.ExecuteReaderAsync(); while (await reader.ReadAsync()) { singerIds.Add(reader.GetFieldValue <int>("SingerId")); } return(singerIds); }
public static IEnumerable <object[]> GetSpannerStringConversions() { yield return(new object[] { "STRING", SpannerDbType.String }); yield return(new object[] { "STRING(MAX)", SpannerDbType.String }); yield return(new object[] { "BOOL", SpannerDbType.Bool }); yield return(new object[] { "BYTES", SpannerDbType.Bytes }); yield return(new object[] { "DATE", SpannerDbType.Date }); yield return(new object[] { "FLOAT64", SpannerDbType.Float64 }); yield return(new object[] { "INT64", SpannerDbType.Int64 }); yield return(new object[] { "TIMESTAMP", SpannerDbType.Timestamp }); yield return(new object[] { " STRING ", SpannerDbType.String }); yield return(new object[] { " BOOL ", SpannerDbType.Bool }); yield return(new object[] { " BYTES ", SpannerDbType.Bytes }); yield return(new object[] { " DATE ", SpannerDbType.Date }); yield return(new object[] { " FLOAT64 ", SpannerDbType.Float64 }); yield return(new object[] { " INT64 ", SpannerDbType.Int64 }); yield return(new object[] { " TIMESTAMP ", SpannerDbType.Timestamp }); yield return(new object[] { "STRING(2)", SpannerDbType.String.WithSize(2) }); yield return(new object[] { "STRING(100)", SpannerDbType.String.WithSize(100) }); yield return(new object[] { " STRING ( 100 ) ", SpannerDbType.String.WithSize(100) }); yield return(new object[] { "STRING ( MAX )", SpannerDbType.String }); yield return(new object[] { "STRING(100)", SpannerDbType.String, false }); yield return(new object[] { "BYTES(100)", SpannerDbType.Bytes.WithSize(100) }); yield return(new object[] { "BYTES(100)", SpannerDbType.Bytes, false }); yield return(new object[] { " BYTES ( 100 ) ", SpannerDbType.Bytes.WithSize(100) }); //some common array types. yield return(new object[] { "ARRAY<STRING>", SpannerDbType.ArrayOf(SpannerDbType.String) }); yield return(new object[] { "ARRAY<STRING(MAX)>", SpannerDbType.ArrayOf(SpannerDbType.String) }); yield return(new object[] { "ARRAY<STRING(5)>", SpannerDbType.ArrayOf(SpannerDbType.String.WithSize(5)) }); yield return(new object[] { "ARRAY<STRING>", SpannerDbType.ArrayOf(SpannerDbType.String) }); yield return(new object[] { "ARRAY<BOOL>", SpannerDbType.ArrayOf(SpannerDbType.Bool) }); yield return(new object[] { "ARRAY<BYTES>", SpannerDbType.ArrayOf(SpannerDbType.Bytes) }); yield return(new object[] { "ARRAY<BYTES(100)>", SpannerDbType.ArrayOf(SpannerDbType.Bytes.WithSize(100)) }); yield return(new object[] { "ARRAY<DATE>", SpannerDbType.ArrayOf(SpannerDbType.Date) }); yield return(new object[] { "ARRAY<FLOAT64>", SpannerDbType.ArrayOf(SpannerDbType.Float64) }); yield return(new object[] { "ARRAY<INT64>", SpannerDbType.ArrayOf(SpannerDbType.Int64) }); yield return(new object[] { "ARRAY<TIMESTAMP>", SpannerDbType.ArrayOf(SpannerDbType.Timestamp) }); yield return(new object[] { "ARRAY<STRING(5)>", SpannerDbType.ArrayOf(SpannerDbType.String), false }); yield return(new object[] { "ARRAY<BYTES(5)>", SpannerDbType.ArrayOf(SpannerDbType.Bytes), false }); yield return(new object[] { "ARRAY < STRING ( 5 )>", SpannerDbType.ArrayOf(SpannerDbType.String.WithSize(5)) }); yield return(new object[] { "ARRAY< STRING ( 5 ) > ", SpannerDbType.ArrayOf(SpannerDbType.String.WithSize(5)) }); // Structs // Empty struct yield return(new object[] { "STRUCT<>", new SpannerStruct().GetSpannerDbType() }); // Non-unique fields var sampleStruct = new SpannerStruct { { "F", SpannerDbType.String, null }, { "F", SpannerDbType.Int64, null }, { "G", SpannerDbType.String, null }, }; yield return(new object[] { "STRUCT<F:STRING,F:INT64,G:STRING>", sampleStruct.GetSpannerDbType() }); // Empty fields sampleStruct = new SpannerStruct { { "", SpannerDbType.String, null }, { "F", SpannerDbType.Int64, null }, { "", SpannerDbType.String, null }, }; yield return(new object[] { "STRUCT<STRING,F:INT64,STRING>", sampleStruct.GetSpannerDbType() }); sampleStruct = new SpannerStruct { { "F1", SpannerDbType.String, null }, { "F2", SpannerDbType.Int64, null }, }; yield return(new object[] { "STRUCT<F1:STRING,F2:INT64>", sampleStruct.GetSpannerDbType() }); yield return(new object[] { "STRUCT< F1 : STRING , F2 : INT64 >", sampleStruct.GetSpannerDbType() }); sampleStruct = new SpannerStruct { { "F1", SpannerDbType.String, null }, { "F2", SpannerDbType.Int64, null }, { "F3", SpannerDbType.Bool, null }, { "F4", SpannerDbType.Bytes, null }, { "F5", SpannerDbType.Date, null }, { "F6", SpannerDbType.Float64, null }, { "F7", SpannerDbType.Timestamp, null }, }; yield return(new object[] { "STRUCT<F1:STRING,F2:INT64,F3:BOOL,F4:BYTES,F5:DATE,F6:FLOAT64,F7:TIMESTAMP>", sampleStruct.GetSpannerDbType() }); sampleStruct = new SpannerStruct { { "F1", SpannerDbType.String.WithSize(100), null }, { "F2", SpannerDbType.Bytes.WithSize(200), null }, }; yield return(new object[] { "STRUCT<F1: STRING(100), F2: BYTES(200)>", sampleStruct.GetSpannerDbType() }); // Struct of struct var nestedStruct = new SpannerStruct { { "F2", SpannerDbType.Int64, null } }; sampleStruct = new SpannerStruct { { "F1", nestedStruct.GetSpannerDbType(), null } }; yield return(new object[] { "STRUCT<F1:STRUCT<F2:INT64>>", sampleStruct.GetSpannerDbType() }); // Struct of array sampleStruct = new SpannerStruct { { "F1", SpannerDbType.ArrayOf(SpannerDbType.Int64), null } }; yield return(new object[] { "STRUCT<F1:ARRAY<INT64>>", sampleStruct.GetSpannerDbType() }); // Array of struct sampleStruct = new SpannerStruct { { "F1", SpannerDbType.Int64, null } }; yield return(new object[] { "ARRAY<STRUCT<F1:INT64>>", SpannerDbType.ArrayOf(sampleStruct.GetSpannerDbType()) }); }
// This data serves as inputs to converting from CLR types that a developer // would use in his app to a given json serialized format using the spanner type // to determine how to serialize. // The inputs are used to test both ways (also deseralizing the generated json // to a requested clr type). However some cases are specified as only one direction // usually because the conversion is by definition lossy. public static IEnumerable <object[]> GetValidValueConversions() { // Format is: LocalClrInstance, SpannerType, SerializedJsonFromProto, [test one or both ways] // Testing can be one way if there is loss of information in the conversion. // Spanner type = Float64 tests. yield return(new object[] { true, SpannerDbType.Float64, "1" }); yield return(new object[] { false, SpannerDbType.Float64, "0" }); yield return(new object[] { (byte)1, SpannerDbType.Float64, "1" }); yield return(new object[] { (sbyte)1, SpannerDbType.Float64, "1" }); yield return(new object[] { 1.5M, SpannerDbType.Float64, "1.5" }); yield return(new object[] { 1.5D, SpannerDbType.Float64, "1.5" }); yield return(new object[] { 1.5F, SpannerDbType.Float64, "1.5" }); yield return(new object[] { double.NegativeInfinity, SpannerDbType.Float64, Quote("-Infinity") }); yield return(new object[] { double.PositiveInfinity, SpannerDbType.Float64, Quote("Infinity") }); yield return(new object[] { double.NaN, SpannerDbType.Float64, Quote("NaN") }); yield return(new object[] { 1, SpannerDbType.Float64, "1" }); yield return(new object[] { 1U, SpannerDbType.Float64, "1" }); yield return(new object[] { 1L, SpannerDbType.Float64, "1" }); yield return(new object[] { (ulong)1, SpannerDbType.Float64, "1" }); yield return(new object[] { (short)1, SpannerDbType.Float64, "1" }); yield return(new object[] { (ushort)1, SpannerDbType.Float64, "1" }); yield return(new object[] { "1", SpannerDbType.Float64, "1" }); yield return(new object[] { "1.5", SpannerDbType.Float64, "1.5" }); yield return(new object[] { DBNull.Value, SpannerDbType.Float64, "null" }); // Spanner type = Int64 tests. yield return(new object[] { true, SpannerDbType.Int64, Quote("1") }); yield return(new object[] { false, SpannerDbType.Int64, Quote("0") }); yield return(new object[] { (char)1, SpannerDbType.Int64, Quote("1") }); yield return(new object[] { (byte)1, SpannerDbType.Int64, Quote("1") }); yield return(new object[] { (sbyte)1, SpannerDbType.Int64, Quote("1") }); yield return(new object[] { 2M, SpannerDbType.Int64, Quote("2") }); yield return(new object[] { 1.5M, SpannerDbType.Int64, Quote("2"), TestType.ClrToValue }); yield return(new object[] { 2D, SpannerDbType.Int64, Quote("2") }); yield return(new object[] { 1.5D, SpannerDbType.Int64, Quote("2"), TestType.ClrToValue }); yield return(new object[] { 2F, SpannerDbType.Int64, Quote("2") }); yield return(new object[] { 1.5F, SpannerDbType.Int64, Quote("2"), TestType.ClrToValue }); yield return(new object[] { 1, SpannerDbType.Int64, Quote("1") }); yield return(new object[] { 1U, SpannerDbType.Int64, Quote("1") }); yield return(new object[] { 1L, SpannerDbType.Int64, Quote("1") }); yield return(new object[] { (ulong)1, SpannerDbType.Int64, Quote("1") }); yield return(new object[] { (short)1, SpannerDbType.Int64, Quote("1") }); yield return(new object[] { (ushort)1, SpannerDbType.Int64, Quote("1") }); yield return(new object[] { "1", SpannerDbType.Int64, Quote("1") }); // Spanner type = Bool tests. yield return(new object[] { true, SpannerDbType.Bool, "true" }); yield return(new object[] { false, SpannerDbType.Bool, "false" }); yield return(new object[] { (byte)1, SpannerDbType.Bool, "true" }); yield return(new object[] { (byte)0, SpannerDbType.Bool, "false" }); yield return(new object[] { (sbyte)1, SpannerDbType.Bool, "true" }); yield return(new object[] { (sbyte)0, SpannerDbType.Bool, "false" }); yield return(new object[] { 1M, SpannerDbType.Bool, "true" }); yield return(new object[] { 6.5M, SpannerDbType.Bool, "true", TestType.ClrToValue }); yield return(new object[] { 0M, SpannerDbType.Bool, "false" }); yield return(new object[] { 1D, SpannerDbType.Bool, "true" }); yield return(new object[] { 6.5D, SpannerDbType.Bool, "true", TestType.ClrToValue }); yield return(new object[] { 0D, SpannerDbType.Bool, "false" }); yield return(new object[] { 1F, SpannerDbType.Bool, "true" }); yield return(new object[] { 6.5F, SpannerDbType.Bool, "true", TestType.ClrToValue }); yield return(new object[] { 0F, SpannerDbType.Bool, "false" }); yield return(new object[] { 1, SpannerDbType.Bool, "true" }); yield return(new object[] { 11, SpannerDbType.Bool, "true", TestType.ClrToValue }); yield return(new object[] { 0, SpannerDbType.Bool, "false" }); yield return(new object[] { 1U, SpannerDbType.Bool, "true" }); yield return(new object[] { 100U, SpannerDbType.Bool, "true", TestType.ClrToValue }); yield return(new object[] { 0U, SpannerDbType.Bool, "false" }); yield return(new object[] { 1L, SpannerDbType.Bool, "true" }); yield return(new object[] { -1L, SpannerDbType.Bool, "true", TestType.ClrToValue }); yield return(new object[] { 0L, SpannerDbType.Bool, "false" }); yield return(new object[] { (ulong)1, SpannerDbType.Bool, "true" }); yield return(new object[] { (ulong)21, SpannerDbType.Bool, "true", TestType.ClrToValue }); yield return(new object[] { (ulong)0, SpannerDbType.Bool, "false" }); yield return(new object[] { (short)1, SpannerDbType.Bool, "true" }); yield return(new object[] { (short)-1, SpannerDbType.Bool, "true", TestType.ClrToValue }); yield return(new object[] { (short)0, SpannerDbType.Bool, "false" }); yield return(new object[] { (ushort)1, SpannerDbType.Bool, "true" }); yield return(new object[] { (ushort)11, SpannerDbType.Bool, "true", TestType.ClrToValue }); yield return(new object[] { (ushort)0, SpannerDbType.Bool, "false" }); // Spanner type = String tests. // Note the casing on bool->string follows c# bool conversion semantics (by design). yield return(new object[] { true, SpannerDbType.String, Quote("True") }); yield return(new object[] { false, SpannerDbType.String, Quote("False") }); yield return(new object[] { (char)26, SpannerDbType.String, Quote("\\u001a") }); yield return(new object[] { (byte)1, SpannerDbType.String, Quote("1") }); yield return(new object[] { (sbyte)1, SpannerDbType.String, Quote("1") }); yield return(new object[] { 1.5M, SpannerDbType.String, Quote("1.5") }); yield return(new object[] { 1.5D, SpannerDbType.String, Quote("1.5") }); yield return(new object[] { 1.5F, SpannerDbType.String, Quote("1.5") }); yield return(new object[] { 1, SpannerDbType.String, Quote("1") }); yield return(new object[] { 1U, SpannerDbType.String, Quote("1") }); yield return(new object[] { 1L, SpannerDbType.String, Quote("1") }); yield return(new object[] { (ulong)1, SpannerDbType.String, Quote("1") }); yield return(new object[] { (short)1, SpannerDbType.String, Quote("1") }); yield return(new object[] { (ushort)1, SpannerDbType.String, Quote("1") }); yield return(new object[] { s_testDate, SpannerDbType.String, Quote("2017-01-31T03:15:30.5Z") }); // Note the difference in C# conversions from special doubles. yield return(new object[] { double.NegativeInfinity, SpannerDbType.String, Quote("-Infinity") }); yield return(new object[] { double.PositiveInfinity, SpannerDbType.String, Quote("Infinity") }); yield return(new object[] { double.NaN, SpannerDbType.String, Quote("NaN") }); yield return(new object[] { "1.5", SpannerDbType.String, Quote("1.5") }); yield return(new object[] { new ToStringClass("hello"), SpannerDbType.String, Quote("hello"), TestType.ClrToValue }); // Spanner type = Date+Timestamp tests. Some of these are one way due to either a lossy conversion (date loses time) // or a string formatting difference. yield return(new object[] { s_testDate, SpannerDbType.Date, Quote("2017-01-31"), TestType.ClrToValue }); yield return(new object[] { "1/31/2017", SpannerDbType.Date, Quote("2017-01-31"), TestType.ClrToValue }); yield return(new object[] { "1/31/2017 3:15:30 AM", SpannerDbType.Date, Quote("2017-01-31"), TestType.ClrToValue }); yield return(new object[] { s_testDate, SpannerDbType.Timestamp, Quote("2017-01-31T03:15:30.5Z") }); yield return(new object[] { s_testDate.AddTicks(5), SpannerDbType.Timestamp, Quote("2017-01-31T03:15:30.5000005Z") }); yield return(new object[] { "1/31/2017", SpannerDbType.Timestamp, Quote("2017-01-31T00:00:00Z"), TestType.ClrToValue }); yield return(new object[] { "1/31/2017 3:15:30 AM", SpannerDbType.Timestamp, Quote("2017-01-31T03:15:30Z"), TestType.ClrToValue }); yield return(new object[] { "1/31/2017 3:15:30 AM", SpannerDbType.Timestamp, Quote("2017-01-31T03:15:30Z"), TestType.ClrToValue }); // Spanner type = Bytes tests. yield return(new object[] { s_base64Encoded, SpannerDbType.Bytes, Quote(s_base64Encoded) }); yield return(new object[] { s_bytesToEncode, SpannerDbType.Bytes, Quote(s_base64Encoded) }); yield return(new object[] { "passthrubadbytes", SpannerDbType.Bytes, Quote("passthrubadbytes") }); // List test cases (list of type X). yield return(new object[] { new List <string>(GetStringsForArray()), SpannerDbType.ArrayOf(SpannerDbType.String), "[ \"abc\", \"123\", \"def\" ]" }); yield return(new object[] { new List <double>(GetFloatsForArray()), SpannerDbType.ArrayOf(SpannerDbType.Float64), "[ 1, 2, 3 ]" }); yield return(new object[] { new List <int>(GetIntsForArray()), SpannerDbType.ArrayOf(SpannerDbType.Int64), "[ \"4\", \"5\", \"6\" ]" }); yield return(new object[] { new List <bool>(GetBoolsForArray()), SpannerDbType.ArrayOf(SpannerDbType.Bool), "[ true, false, true ]" }); yield return(new object[] { new List <DateTime>(GetDatesForArray()), SpannerDbType.ArrayOf(SpannerDbType.Date), "[ \"2017-01-31\", \"2016-02-15\", \"2015-03-31\" ]" }); yield return(new object[] { new List <DateTime>(GetTimestampsForArray()), SpannerDbType.ArrayOf(SpannerDbType.Timestamp), "[ \"2017-01-31T03:15:30.5Z\", \"2016-02-15T13:15:30Z\", \"2015-03-31T03:15:30.25Z\" ]" }); // List test cases (various source/target list types) yield return(new object[] { GetStringsForArray(), SpannerDbType.ArrayOf(SpannerDbType.String), "[ \"abc\", \"123\", \"def\" ]", TestType.ClrToValue }); #pragma warning disable DE0006 // ArrayList deprecation yield return(new object[] { new ArrayList(GetStringsForArray().ToList()), SpannerDbType.ArrayOf(SpannerDbType.String), "[ \"abc\", \"123\", \"def\" ]" }); #pragma warning restore DE0006 yield return(new object[] { new List <object>(GetStringsForArray()), SpannerDbType.ArrayOf(SpannerDbType.String), "[ \"abc\", \"123\", \"def\" ]" }); yield return(new object[] { new CustomList(GetStringsForArray()), SpannerDbType.ArrayOf(SpannerDbType.String), "[ \"abc\", \"123\", \"def\" ]" }); // List test cases with null entries yield return(new object[] { new List <string> { "x", null, "y" }, SpannerDbType.ArrayOf(SpannerDbType.String), "[ \"x\", null, \"y\" ]" }); yield return(new object[] { new double?[] { 5.5, null, 10.5 }, SpannerDbType.ArrayOf(SpannerDbType.Float64), "[ 5.5, null, 10.5 ]" }); // Struct test case includes nested complex conversions. // These are one-way only, because SpannerStruct doesn't (and can't generally) implement // IEquatable<T>. (By the time the nested value can be an array etc, it ends up being infeasible.) // Deserialization is therefore handled separately in hand-written tests. yield return(new object[] { s_sampleStruct, s_sampleStruct.GetSpannerDbType(), s_sampleStructSerialized, TestType.ClrToValue }); // Array of structs. yield return(new object[] { new List <object>(new[] { s_sampleStruct }), SpannerDbType.ArrayOf(s_sampleStruct.GetSpannerDbType()), $"[ {s_sampleStructSerialized} ]", TestType.ClrToValue }); // Struct of struct and array. var complexStruct = new SpannerStruct { { "StructField", s_sampleStruct.GetSpannerDbType(), s_sampleStruct }, { "ArrayField", SpannerDbType.ArrayOf(SpannerDbType.Int64), GetIntsForArray().Select(x => (long)x).ToList() } }; yield return(new object[] { complexStruct, complexStruct.GetSpannerDbType(), $"[ {s_sampleStructSerialized}, [ \"4\", \"5\", \"6\" ] ]", TestType.ClrToValue }); }