diff --git a/google-cloud-bigquerystorage/src/main/java/com/google/cloud/bigquery/storage/v1beta2/BQTableSchemaToProtoDescriptor.java b/google-cloud-bigquerystorage/src/main/java/com/google/cloud/bigquery/storage/v1beta2/BQTableSchemaToProtoDescriptor.java index e4aa97798f..ec9083fefa 100644 --- a/google-cloud-bigquerystorage/src/main/java/com/google/cloud/bigquery/storage/v1beta2/BQTableSchemaToProtoDescriptor.java +++ b/google-cloud-bigquerystorage/src/main/java/com/google/cloud/bigquery/storage/v1beta2/BQTableSchemaToProtoDescriptor.java @@ -51,7 +51,7 @@ public class BQTableSchemaToProtoDescriptor { .put(TableFieldSchema.Type.DOUBLE, FieldDescriptorProto.Type.TYPE_DOUBLE) .put(TableFieldSchema.Type.GEOGRAPHY, FieldDescriptorProto.Type.TYPE_STRING) .put(TableFieldSchema.Type.INT64, FieldDescriptorProto.Type.TYPE_INT64) - .put(TableFieldSchema.Type.NUMERIC, FieldDescriptorProto.Type.TYPE_STRING) + .put(TableFieldSchema.Type.NUMERIC, FieldDescriptorProto.Type.TYPE_BYTES) .put(TableFieldSchema.Type.STRING, FieldDescriptorProto.Type.TYPE_STRING) .put(TableFieldSchema.Type.STRUCT, FieldDescriptorProto.Type.TYPE_MESSAGE) .put(TableFieldSchema.Type.TIME, FieldDescriptorProto.Type.TYPE_INT64) diff --git a/google-cloud-bigquerystorage/src/main/java/com/google/cloud/bigquery/storage/v1beta2/JsonToProtoMessage.java b/google-cloud-bigquerystorage/src/main/java/com/google/cloud/bigquery/storage/v1beta2/JsonToProtoMessage.java index a560495841..7952b11695 100644 --- a/google-cloud-bigquerystorage/src/main/java/com/google/cloud/bigquery/storage/v1beta2/JsonToProtoMessage.java +++ b/google-cloud-bigquerystorage/src/main/java/com/google/cloud/bigquery/storage/v1beta2/JsonToProtoMessage.java @@ -17,6 +17,7 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; +import com.google.protobuf.ByteString; import com.google.protobuf.Descriptors.Descriptor; import com.google.protobuf.Descriptors.FieldDescriptor; import com.google.protobuf.DynamicMessage; @@ -138,7 +139,10 @@ private static void fillField( } break; case BYTES: - if (val instanceof String) { + if (val instanceof ByteString) { + protoMsg.setField(fieldDescriptor, ((ByteString) val).toByteArray()); + return; + } else if (val instanceof String) { protoMsg.setField(fieldDescriptor, ((String) val).getBytes()); return; } @@ -234,7 +238,25 @@ private static void fillRepeatedField( break; case BYTES: if (val instanceof String) { + // TODO(jstocklass): If string, decode it and pass in the byte array. Will need to + // update tests to ensure that strings passed in are properly encoded as well. protoMsg.addRepeatedField(fieldDescriptor, ((String) val).getBytes()); + } else if (val instanceof JSONArray) { + try { + byte[] bytes = new byte[((JSONArray) val).length()]; + for (int j = 0; j < ((JSONArray) val).length(); j++) { + bytes[j] = (byte) ((byte) (((JSONArray) val).get(j)) & 0xFF); + } + protoMsg.addRepeatedField(fieldDescriptor, bytes); + } catch (ClassCastException e) { + throw new IllegalArgumentException( + String.format( + "Error: " + + currentScope + + "[" + + index + + "] could not be converted to byte[].")); + } } else { fail = true; } diff --git a/google-cloud-bigquerystorage/src/test/java/com/google/cloud/bigquery/storage/v1alpha2/BQTableSchemaToProtoDescriptorTest.java b/google-cloud-bigquerystorage/src/test/java/com/google/cloud/bigquery/storage/v1alpha2/BQTableSchemaToProtoDescriptorTest.java index 63c51cf56c..b4f43f80ed 100644 --- a/google-cloud-bigquerystorage/src/test/java/com/google/cloud/bigquery/storage/v1alpha2/BQTableSchemaToProtoDescriptorTest.java +++ b/google-cloud-bigquerystorage/src/test/java/com/google/cloud/bigquery/storage/v1alpha2/BQTableSchemaToProtoDescriptorTest.java @@ -20,6 +20,7 @@ import com.google.cloud.bigquery.storage.test.JsonTest.*; import com.google.cloud.bigquery.storage.test.SchemaTest.*; +import com.google.cloud.bigquery.storage.v1alpha2.Table.TableFieldSchema; import com.google.common.collect.ImmutableMap; import com.google.protobuf.Descriptors.Descriptor; import com.google.protobuf.Descriptors.FieldDescriptor; @@ -183,7 +184,7 @@ public void testStructComplex() throws Exception { .build(); final Table.TableFieldSchema TEST_NUMERIC = Table.TableFieldSchema.newBuilder() - .setType(Table.TableFieldSchema.Type.NUMERIC) + .setType(TableFieldSchema.Type.BYTES) .setMode(Table.TableFieldSchema.Mode.NULLABLE) .setName("test_numeric") .build(); diff --git a/google-cloud-bigquerystorage/src/test/java/com/google/cloud/bigquery/storage/v1beta2/BQTableSchemaToProtoDescriptorTest.java b/google-cloud-bigquerystorage/src/test/java/com/google/cloud/bigquery/storage/v1beta2/BQTableSchemaToProtoDescriptorTest.java index e5d0676975..95b5934378 100644 --- a/google-cloud-bigquerystorage/src/test/java/com/google/cloud/bigquery/storage/v1beta2/BQTableSchemaToProtoDescriptorTest.java +++ b/google-cloud-bigquerystorage/src/test/java/com/google/cloud/bigquery/storage/v1beta2/BQTableSchemaToProtoDescriptorTest.java @@ -42,7 +42,7 @@ public class BQTableSchemaToProtoDescriptorTest { .put(TableFieldSchema.Type.DOUBLE, DoubleType.getDescriptor()) .put(TableFieldSchema.Type.GEOGRAPHY, StringType.getDescriptor()) .put(TableFieldSchema.Type.INT64, Int64Type.getDescriptor()) - .put(TableFieldSchema.Type.NUMERIC, StringType.getDescriptor()) + .put(TableFieldSchema.Type.NUMERIC, BytesType.getDescriptor()) .put(TableFieldSchema.Type.STRING, StringType.getDescriptor()) .put(TableFieldSchema.Type.TIME, Int64Type.getDescriptor()) .put(TableFieldSchema.Type.TIMESTAMP, Int64Type.getDescriptor()) diff --git a/google-cloud-bigquerystorage/src/test/java/com/google/cloud/bigquery/storage/v1beta2/JsonStreamWriterTest.java b/google-cloud-bigquerystorage/src/test/java/com/google/cloud/bigquery/storage/v1beta2/JsonStreamWriterTest.java index 1205aa9615..966a074fa1 100644 --- a/google-cloud-bigquerystorage/src/test/java/com/google/cloud/bigquery/storage/v1beta2/JsonStreamWriterTest.java +++ b/google-cloud-bigquerystorage/src/test/java/com/google/cloud/bigquery/storage/v1beta2/JsonStreamWriterTest.java @@ -36,6 +36,7 @@ import com.google.protobuf.Int64Value; import com.google.protobuf.Timestamp; import java.io.IOException; +import java.math.BigDecimal; import java.util.*; import java.util.concurrent.ExecutionException; import java.util.logging.Logger; @@ -408,7 +409,8 @@ public void testSingleAppendComplexJson() throws Exception { com.google.cloud.bigquery.storage.test.JsonTest.ComplexLvl2.newBuilder() .setTestInt(3) .build()) - .setTestNumeric("1.23456") + .setTestNumeric( + BigDecimalByteStringEncoder.encodeToNumericByteString(new BigDecimal("1.23456"))) .setTestGeo("POINT(1,1)") .setTestTimestamp(12345678) .setTestTime(CivilTimeEncoder.encodePacked64TimeMicros(LocalTime.of(1, 0, 1))) @@ -429,7 +431,9 @@ public void testSingleAppendComplexJson() throws Exception { json.put("test_date", 1); json.put("complex_lvl1", complex_lvl1); json.put("complex_lvl2", complex_lvl2); - json.put("test_numeric", "1.23456"); + json.put( + "test_numeric", + BigDecimalByteStringEncoder.encodeToNumericByteString(new BigDecimal("1.23456"))); json.put("test_geo", "POINT(1,1)"); json.put("test_timestamp", 12345678); json.put("test_time", CivilTimeEncoder.encodePacked64TimeMicros(LocalTime.of(1, 0, 1))); diff --git a/google-cloud-bigquerystorage/src/test/java/com/google/cloud/bigquery/storage/v1beta2/JsonToProtoMessageTest.java b/google-cloud-bigquerystorage/src/test/java/com/google/cloud/bigquery/storage/v1beta2/JsonToProtoMessageTest.java index 16cf5be087..c652200a5b 100644 --- a/google-cloud-bigquerystorage/src/test/java/com/google/cloud/bigquery/storage/v1beta2/JsonToProtoMessageTest.java +++ b/google-cloud-bigquerystorage/src/test/java/com/google/cloud/bigquery/storage/v1beta2/JsonToProtoMessageTest.java @@ -16,6 +16,7 @@ package com.google.cloud.bigquery.storage.v1beta2; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import com.google.cloud.bigquery.storage.test.JsonTest.*; import com.google.cloud.bigquery.storage.test.SchemaTest.*; @@ -24,8 +25,10 @@ import com.google.protobuf.Descriptors.Descriptor; import com.google.protobuf.DynamicMessage; import com.google.protobuf.Message; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Map; +import java.util.logging.Logger; import org.json.JSONArray; import org.json.JSONObject; import org.junit.Assert; @@ -35,6 +38,7 @@ @RunWith(JUnit4.class) public class JsonToProtoMessageTest { + private static final Logger LOG = Logger.getLogger(JsonToProtoMessageTest.class.getName()); private static ImmutableMap AllTypesToDebugMessageTest = new ImmutableMap.Builder() .put(BoolType.getDescriptor(), "boolean") @@ -97,15 +101,15 @@ public class JsonToProtoMessageTest { }) .build(); - private static ImmutableMap AllRepeatedTypesToDebugMessageTest = - new ImmutableMap.Builder() - .put(RepeatedBool.getDescriptor(), "boolean") - .put(RepeatedBytes.getDescriptor(), "string") - .put(RepeatedInt64.getDescriptor(), "int64") - .put(RepeatedInt32.getDescriptor(), "int32") - .put(RepeatedDouble.getDescriptor(), "double") - .put(RepeatedString.getDescriptor(), "string") - .put(RepeatedObject.getDescriptor(), "object") + private static ImmutableMap AllRepeatedTypesToDebugMessageTest = + new ImmutableMap.Builder() + .put(RepeatedBool.getDescriptor(), new String[] {"boolean"}) + .put(RepeatedBytes.getDescriptor(), new String[] {"string", "bytes"}) + .put(RepeatedInt64.getDescriptor(), new String[] {"int64"}) + .put(RepeatedInt32.getDescriptor(), new String[] {"int32"}) + .put(RepeatedDouble.getDescriptor(), new String[] {"double"}) + .put(RepeatedString.getDescriptor(), new String[] {"string"}) + .put(RepeatedObject.getDescriptor(), new String[] {"object"}) .build(); private static ImmutableMap AllRepeatedTypesToCorrectProto = @@ -121,6 +125,13 @@ public class JsonToProtoMessageTest { RepeatedBytes.newBuilder() .addTestRepeated(ByteString.copyFrom("hello".getBytes())) .addTestRepeated(ByteString.copyFrom("test".getBytes())) + .build(), + RepeatedBytes.newBuilder() + .addTestRepeated( + BigDecimalByteStringEncoder.encodeToNumericByteString(new BigDecimal("0"))) + .addTestRepeated( + BigDecimalByteStringEncoder.encodeToNumericByteString( + new BigDecimal("1.2"))) .build() }) .put( @@ -243,6 +254,18 @@ public class JsonToProtoMessageTest { .put("test_repeated", new JSONArray(new Float[] {Float.MAX_VALUE, Float.MIN_VALUE})), new JSONObject().put("test_repeated", new JSONArray(new Boolean[] {true, false})), new JSONObject().put("test_repeated", new JSONArray(new String[] {"hello", "test"})), + new JSONObject() + .put( + "test_repeated", + new JSONArray( + new byte[][] { + BigDecimalByteStringEncoder.encodeToNumericByteString(new BigDecimal("0")) + .toByteArray(), + BigDecimalByteStringEncoder.encodeToNumericByteString(new BigDecimal("1.2")) + .toByteArray() + })), + new JSONObject().put("test_repeated", new JSONArray(new char[][] {{'a', 'b'}, {'c'}})), + new JSONObject().put("test_repeated", new JSONArray(new String[][] {{"hello"}, {"test"}})), new JSONObject() .put( "test_repeated", @@ -347,7 +370,7 @@ public void testAllTypes() throws Exception { @Test public void testAllRepeatedTypesWithLimits() throws Exception { - for (Map.Entry entry : AllRepeatedTypesToDebugMessageTest.entrySet()) { + for (Map.Entry entry : AllRepeatedTypesToDebugMessageTest.entrySet()) { int success = 0; for (JSONObject json : simpleJSONArrays) { try { @@ -356,13 +379,19 @@ public void testAllRepeatedTypesWithLimits() throws Exception { assertEquals(protoMsg, AllRepeatedTypesToCorrectProto.get(entry.getKey())[success]); success += 1; } catch (IllegalArgumentException e) { - assertEquals( - "JSONObject does not have a " + entry.getValue() + " field at root.test_repeated[0].", - e.getMessage()); + assertTrue( + e.getMessage() + .equals( + "JSONObject does not have a " + + entry.getValue()[0] + + " field at root.test_repeated[0].") + || e.getMessage() + .equals("Error: root.test_repeated[0] could not be converted to byte[].")); } } if (entry.getKey() == RepeatedInt64.getDescriptor() - || entry.getKey() == RepeatedDouble.getDescriptor()) { + || entry.getKey() == RepeatedDouble.getDescriptor() + || entry.getKey() == RepeatedBytes.getDescriptor()) { assertEquals(2, success); } else { assertEquals(1, success); diff --git a/google-cloud-bigquerystorage/src/test/java/com/google/cloud/bigquery/storage/v1beta2/it/ITBigQueryBigDecimalByteStringEncoderTest.java b/google-cloud-bigquerystorage/src/test/java/com/google/cloud/bigquery/storage/v1beta2/it/ITBigQueryBigDecimalByteStringEncoderTest.java new file mode 100644 index 0000000000..e61354363a --- /dev/null +++ b/google-cloud-bigquerystorage/src/test/java/com/google/cloud/bigquery/storage/v1beta2/it/ITBigQueryBigDecimalByteStringEncoderTest.java @@ -0,0 +1,158 @@ +/* + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigquery.storage.v1beta2.it; + +import static org.junit.Assert.assertEquals; + +import com.google.api.core.ApiFuture; +import com.google.cloud.ServiceOptions; +import com.google.cloud.bigquery.BigQuery; +import com.google.cloud.bigquery.DatasetInfo; +import com.google.cloud.bigquery.Field.Mode; +import com.google.cloud.bigquery.FieldValueList; +import com.google.cloud.bigquery.Schema; +import com.google.cloud.bigquery.StandardSQLTypeName; +import com.google.cloud.bigquery.StandardTableDefinition; +import com.google.cloud.bigquery.TableId; +import com.google.cloud.bigquery.TableInfo; +import com.google.cloud.bigquery.TableResult; +import com.google.cloud.bigquery.storage.v1beta2.AppendRowsResponse; +import com.google.cloud.bigquery.storage.v1beta2.AppendRowsResponse.AppendResult; +import com.google.cloud.bigquery.storage.v1beta2.BigDecimalByteStringEncoder; +import com.google.cloud.bigquery.storage.v1beta2.BigQueryWriteClient; +import com.google.cloud.bigquery.storage.v1beta2.JsonStreamWriter; +import com.google.cloud.bigquery.storage.v1beta2.TableName; +import com.google.cloud.bigquery.testing.RemoteBigQueryHelper; +import com.google.protobuf.Descriptors; +import java.io.IOException; +import java.math.BigDecimal; +import java.util.Iterator; +import java.util.concurrent.ExecutionException; +import java.util.logging.Logger; +import org.json.JSONArray; +import org.json.JSONObject; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +public class ITBigQueryBigDecimalByteStringEncoderTest { + private static final Logger LOG = + Logger.getLogger(ITBigQueryBigDecimalByteStringEncoderTest.class.getName()); + private static final String DATASET = RemoteBigQueryHelper.generateDatasetName(); + private static final String TABLE = "testtable"; + private static final String DESCRIPTION = "BigQuery Write Java manual client test dataset"; + + private static BigQueryWriteClient client; + private static TableInfo tableInfo; + private static BigQuery bigquery; + + @BeforeClass + public static void beforeClass() throws IOException { + client = BigQueryWriteClient.create(); + + RemoteBigQueryHelper bigqueryHelper = RemoteBigQueryHelper.create(); + bigquery = bigqueryHelper.getOptions().getService(); + DatasetInfo datasetInfo = + DatasetInfo.newBuilder(/* datasetId = */ DATASET).setDescription(DESCRIPTION).build(); + bigquery.create(datasetInfo); + tableInfo = + TableInfo.newBuilder( + TableId.of(DATASET, TABLE), + StandardTableDefinition.of( + Schema.of( + com.google.cloud.bigquery.Field.newBuilder( + "test_numeric_zero", StandardSQLTypeName.NUMERIC) + .build(), + com.google.cloud.bigquery.Field.newBuilder( + "test_numeric_one", StandardSQLTypeName.NUMERIC) + .build(), + com.google.cloud.bigquery.Field.newBuilder( + "test_numeric_repeated", StandardSQLTypeName.NUMERIC) + .setMode(Mode.REPEATED) + .build()))) + .build(); + bigquery.create(tableInfo); + } + + @AfterClass + public static void afterClass() { + if (client != null) { + client.close(); + } + if (bigquery != null) { + RemoteBigQueryHelper.forceDelete(bigquery, DATASET); + } + } + + @Test + public void TestBigDecimalEncoding() + throws IOException, InterruptedException, ExecutionException, + Descriptors.DescriptorValidationException { + TableName parent = TableName.of(ServiceOptions.getDefaultProjectId(), DATASET, TABLE); + try (JsonStreamWriter jsonStreamWriter = + JsonStreamWriter.newBuilder(parent.toString(), tableInfo.getDefinition().getSchema()) + .createDefaultStream() + .build()) { + JSONObject row = new JSONObject(); + row.put( + "test_numeric_zero", + BigDecimalByteStringEncoder.encodeToNumericByteString(new BigDecimal("0"))); + row.put( + "test_numeric_one", + BigDecimalByteStringEncoder.encodeToNumericByteString(new BigDecimal("1.2"))); + row.put( + "test_numeric_repeated", + new JSONArray( + new byte[][] { + BigDecimalByteStringEncoder.encodeToNumericByteString(new BigDecimal("0")) + .toByteArray(), + BigDecimalByteStringEncoder.encodeToNumericByteString(new BigDecimal("1.2")) + .toByteArray(), + BigDecimalByteStringEncoder.encodeToNumericByteString(new BigDecimal("-1.2")) + .toByteArray(), + BigDecimalByteStringEncoder.encodeToNumericByteString( + new BigDecimal("99999999999999999999999999999.999999999")) + .toByteArray(), + BigDecimalByteStringEncoder.encodeToNumericByteString( + new BigDecimal("-99999999999999999999999999999.999999999")) + .toByteArray(), + })); + JSONArray jsonArr = new JSONArray(new JSONObject[] {row}); + ApiFuture response = jsonStreamWriter.append(jsonArr, -1); + AppendRowsResponse arr = response.get(); + AppendResult ar = arr.getAppendResult(); + boolean ho = ar.hasOffset(); + TableResult result = + bigquery.listTableData( + tableInfo.getTableId(), BigQuery.TableDataListOption.startIndex(0L)); + Iterator iter = result.getValues().iterator(); + FieldValueList currentRow; + currentRow = iter.next(); + assertEquals("0", currentRow.get(0).getStringValue()); + assertEquals("1.2", currentRow.get(1).getStringValue()); + assertEquals("0", currentRow.get(2).getRepeatedValue().get(0).getStringValue()); + assertEquals("1.2", currentRow.get(2).getRepeatedValue().get(1).getStringValue()); + assertEquals("-1.2", currentRow.get(2).getRepeatedValue().get(2).getStringValue()); + assertEquals( + "99999999999999999999999999999.999999999", + currentRow.get(2).getRepeatedValue().get(3).getStringValue()); + assertEquals( + "-99999999999999999999999999999.999999999", + currentRow.get(2).getRepeatedValue().get(4).getStringValue()); + } + } +} diff --git a/google-cloud-bigquerystorage/src/test/java/com/google/cloud/bigquery/storage/v1beta2/it/ITBigQueryWriteManualClientTest.java b/google-cloud-bigquerystorage/src/test/java/com/google/cloud/bigquery/storage/v1beta2/it/ITBigQueryWriteManualClientTest.java index ddfbd08e4d..8d60dd2e96 100644 --- a/google-cloud-bigquerystorage/src/test/java/com/google/cloud/bigquery/storage/v1beta2/it/ITBigQueryWriteManualClientTest.java +++ b/google-cloud-bigquerystorage/src/test/java/com/google/cloud/bigquery/storage/v1beta2/it/ITBigQueryWriteManualClientTest.java @@ -31,6 +31,7 @@ import com.google.protobuf.Descriptors; import com.google.protobuf.Int64Value; import java.io.IOException; +import java.math.BigDecimal; import java.util.*; import java.util.concurrent.*; import java.util.logging.Logger; @@ -240,7 +241,15 @@ public void testJsonStreamWriterCommittedStream() LOG.info("Sending one message"); JSONObject row1 = new JSONObject(); row1.put("test_str", "aaa"); - row1.put("test_numerics", new JSONArray(new String[] {"123.4", "-9000000"})); + row1.put( + "test_numerics", + new JSONArray( + new byte[][] { + BigDecimalByteStringEncoder.encodeToNumericByteString(new BigDecimal("123.4")) + .toByteArray(), + BigDecimalByteStringEncoder.encodeToNumericByteString(new BigDecimal("-9000000")) + .toByteArray() + })); row1.put( "test_datetime", CivilTimeEncoder.encodePacked64DatetimeMicros(LocalDateTime.of(2020, 10, 1, 12, 0))); @@ -315,7 +324,15 @@ public void testJsonStreamWriterWithDefaultStream() LOG.info("Sending one message"); JSONObject row1 = new JSONObject(); row1.put("test_str", "aaa"); - row1.put("test_numerics", new JSONArray(new String[] {"123.4", "-9000000"})); + row1.put( + "test_numerics", + new JSONArray( + new byte[][] { + BigDecimalByteStringEncoder.encodeToNumericByteString(new BigDecimal("123.4")) + .toByteArray(), + BigDecimalByteStringEncoder.encodeToNumericByteString(new BigDecimal("-9000000")) + .toByteArray() + })); row1.put( "test_datetime", CivilTimeEncoder.encodePacked64DatetimeMicros(LocalDateTime.of(2020, 10, 1, 12, 0))); diff --git a/google-cloud-bigquerystorage/src/test/proto/jsonTest.proto b/google-cloud-bigquerystorage/src/test/proto/jsonTest.proto index 2a7643252a..920a1a74b0 100644 --- a/google-cloud-bigquerystorage/src/test/proto/jsonTest.proto +++ b/google-cloud-bigquerystorage/src/test/proto/jsonTest.proto @@ -11,7 +11,7 @@ message ComplexRoot { required int32 test_date = 6; required ComplexLvl1 complex_lvl1 = 7; required ComplexLvl2 complex_lvl2 = 8; - optional string test_numeric = 9; + optional bytes test_numeric = 9; optional string test_geo = 10; optional int64 test_timestamp = 11; optional int64 test_time = 12;