Skip to content

Commit eafb6b0

Browse files
committed
Add support for BYTES data type
1 parent 6080286 commit eafb6b0

File tree

6 files changed

+174
-87
lines changed

6 files changed

+174
-87
lines changed

gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Field.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public static class Type implements Serializable {
7373
private static final long serialVersionUID = 2841484762609576959L;
7474

7575
public enum Value {
76-
STRING, INTEGER, FLOAT, BOOLEAN, TIMESTAMP, RECORD
76+
BYTES, STRING, INTEGER, FLOAT, BOOLEAN, TIMESTAMP, RECORD
7777
}
7878

7979
private final Value value;
@@ -108,6 +108,13 @@ public List<Field> fields() {
108108
return fields;
109109
}
110110

111+
/**
112+
* Returns a {@link Value#BYTES} field value.
113+
*/
114+
public static Type bytes() {
115+
return new Type(Value.BYTES);
116+
}
117+
111118
/**
112119
* Returns a {@link Value#STRING} field value.
113120
*/

gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/FieldValue.java

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.google.common.base.Function;
2424
import com.google.common.base.MoreObjects;
2525
import com.google.common.collect.Lists;
26+
import com.google.common.io.BaseEncoding;
2627

2728
import java.io.Serializable;
2829
import java.util.List;
@@ -54,7 +55,7 @@ public FieldValue apply(Object pb) {
5455
public enum Attribute {
5556
/**
5657
* A primitive field value. A {@code FieldValue} is primitive when the corresponding field has
57-
* type {@link Field.Type#bool()}, {@link Field.Type#string()},
58+
* type {@link Field.Type#bytes()}, {@link Field.Type#bool()}, {@link Field.Type#string()},
5859
* {@link Field.Type#floatingPoint()}, {@link Field.Type#integer()},
5960
* {@link Field.Type#timestamp()} or the value is set to {@code null}.
6061
*/
@@ -80,7 +81,7 @@ public enum Attribute {
8081
* Returns the attribute of this Field Value.
8182
*
8283
* @return {@link Attribute#PRIMITIVE} if the field is a primitive type
83-
* ({@link Field.Type#bool()}, {@link Field.Type#string()},
84+
* ({@link Field.Type#bytes()}, {@link Field.Type#bool()}, {@link Field.Type#string()},
8485
* {@link Field.Type#floatingPoint()}, {@link Field.Type#integer()},
8586
* {@link Field.Type#timestamp()}) or is {@code null}. Returns {@link Attribute#REPEATED} if
8687
* the corresponding field has ({@link Field.Mode#REPEATED}) mode. Returns
@@ -108,8 +109,8 @@ public Object value() {
108109

109110
/**
110111
* Returns this field's value as a {@link String}. This method should only be used if the
111-
* corresponding field has primitive type ({@link Field.Type#bool()}, {@link Field.Type#string()},
112-
* {@link Field.Type#floatingPoint()}, {@link Field.Type#integer()},
112+
* corresponding field has primitive type ({@link Field.Type#bytes()}, {@link Field.Type#bool()},
113+
* {@link Field.Type#string()}, {@link Field.Type#floatingPoint()}, {@link Field.Type#integer()},
113114
* {@link Field.Type#timestamp()}).
114115
*
115116
* @throws ClassCastException if the field is not a primitive type
@@ -121,6 +122,22 @@ public String stringValue() {
121122
return (String) value;
122123
}
123124

125+
/**
126+
* Returns this field's value as a byte array. This method should only be used if the
127+
* corresponding field has primitive type ({@link Field.Type#bytes()}.
128+
*
129+
* @throws ClassCastException if the field is not a primitive type
130+
* @throws NullPointerException if {@link #isNull()} returns {@code true}
131+
* @throws IllegalStateException if the field value is not encoded in base64
132+
*/
133+
public byte[] bytesValue() {
134+
try {
135+
return BaseEncoding.base64().decode(stringValue());
136+
} catch (IllegalArgumentException ex) {
137+
throw new IllegalStateException(ex);
138+
}
139+
}
140+
124141
/**
125142
* Returns this field's value as a {@code long}. This method should only be used if the
126143
* corresponding field has {@link Field.Type#integer()} type.

gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/InsertAllRequest.java

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ public final class InsertAllRequest implements Serializable {
4949

5050
/**
5151
* A Google Big Query row to be inserted into a table. Each {@code RowToInsert} has an associated
52-
* id used by BigQuery to detect duplicate insertion requests on a best-effort basis.
52+
* id used by BigQuery to detect duplicate insertion requests on a best-effort basis. Please
53+
* notice that data for fields of type {@link Field.Type#bytes()} must be provided as a base64
54+
* encoded string.
5355
*
5456
* <p>Example usage of creating a row to insert:
5557
* <pre> {@code
@@ -58,8 +60,9 @@ public final class InsertAllRequest implements Serializable {
5860
* recordContent.put("subfieldName1", "value");
5961
* recordContent.put("subfieldName2", repeatedFieldValue);
6062
* Map<String, Object> rowContent = new HashMap<String, Object>();
61-
* rowContent.put("fieldName1", true);
62-
* rowContent.put("fieldName2", recordContent);
63+
* rowContent.put("booleanFieldName", true);
64+
* rowContent.put("bytesFieldName", "DQ4KDQ==");
65+
* rowContent.put("recordFieldName", recordContent);
6366
* RowToInsert row = new RowToInsert("rowId", rowContent);
6467
* }</pre>
6568
*
@@ -116,7 +119,8 @@ public boolean equals(Object obj) {
116119
}
117120

118121
/**
119-
* Creates a row to be inserted with associated id.
122+
* Creates a row to be inserted with associated id. Please notice that data for fields of type
123+
* {@link Field.Type#bytes()} must be provided as a base64 encoded string.
120124
*
121125
* @param id id of the row, used to identify duplicates
122126
* @param content the actual content of the row
@@ -126,7 +130,8 @@ public static RowToInsert of(String id, Map<String, Object> content) {
126130
}
127131

128132
/**
129-
* Creates a row to be inserted without associated id.
133+
* Creates a row to be inserted without associated id. Please notice that data for fields of
134+
* type {@link Field.Type#bytes()} must be provided as a base64 encoded string.
130135
*
131136
* @param content the actual content of the row
132137
*/
@@ -174,7 +179,8 @@ public Builder addRow(RowToInsert rowToInsert) {
174179
}
175180

176181
/**
177-
* Adds a row to be inserted with associated id.
182+
* Adds a row to be inserted with associated id. Please notice that data for fields of type
183+
* {@link Field.Type#bytes()} must be provided as a base64 encoded string.
178184
*
179185
* <p>Example usage of adding a row with associated id:
180186
* <pre> {@code
@@ -184,8 +190,9 @@ public Builder addRow(RowToInsert rowToInsert) {
184190
* recordContent.put("subfieldName1", "value");
185191
* recordContent.put("subfieldName2", repeatedFieldValue);
186192
* Map<String, Object> rowContent = new HashMap<String, Object>();
187-
* rowContent.put("fieldName1", true);
188-
* rowContent.put("fieldName2", recordContent);
193+
* rowContent.put("booleanFieldName", true);
194+
* rowContent.put("bytesFieldName", "DQ4KDQ==");
195+
* rowContent.put("recordFieldName", recordContent);
189196
* builder.addRow("rowId", rowContent);
190197
* }</pre>
191198
*/
@@ -195,7 +202,8 @@ public Builder addRow(String id, Map<String, Object> content) {
195202
}
196203

197204
/**
198-
* Adds a row to be inserted without an associated id.
205+
* Adds a row to be inserted without an associated id. Please notice that data for fields of
206+
* type {@link Field.Type#bytes()} must be provided as a base64 encoded string.
199207
*
200208
* <p>Example usage of adding a row without an associated id:
201209
* <pre> {@code
@@ -205,8 +213,9 @@ public Builder addRow(String id, Map<String, Object> content) {
205213
* recordContent.put("subfieldName1", "value");
206214
* recordContent.put("subfieldName2", repeatedFieldValue);
207215
* Map<String, Object> rowContent = new HashMap<String, Object>();
208-
* rowContent.put("fieldName1", true);
209-
* rowContent.put("fieldName2", recordContent);
216+
* rowContent.put("booleanFieldName", true);
217+
* rowContent.put("bytesFieldName", "DQ4KDQ==");
218+
* rowContent.put("recordFieldName", recordContent);
210219
* builder.addRow(rowContent);
211220
* }</pre>
212221
*/

gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/FieldValueTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.google.cloud.bigquery;
1818

19+
import static org.junit.Assert.assertArrayEquals;
1920
import static org.junit.Assert.assertEquals;
2021
import static org.junit.Assert.assertFalse;
2122
import static org.junit.Assert.assertNull;
@@ -24,18 +25,22 @@
2425
import com.google.api.services.bigquery.model.TableCell;
2526
import com.google.common.collect.ImmutableList;
2627
import com.google.common.collect.ImmutableMap;
28+
import com.google.common.io.BaseEncoding;
2729

2830
import org.junit.Test;
2931

3032
import java.util.Map;
3133

3234
public class FieldValueTest {
3335

36+
private static final byte[] BYTES = {0xD, 0xE, 0xA, 0xD};
37+
private static final String BYTES_BASE64 = BaseEncoding.base64().encode(BYTES);
3438
private static final TableCell BOOLEAN_FIELD = new TableCell().setV("false");
3539
private static final Map<String, String> INTEGER_FIELD = ImmutableMap.of("v", "1");
3640
private static final Map<String, String> FLOAT_FIELD = ImmutableMap.of("v", "1.5");
3741
private static final Map<String, String> STRING_FIELD = ImmutableMap.of("v", "string");
3842
private static final Map<String, String> TIMESTAMP_FIELD = ImmutableMap.of("v", "42");
43+
private static final Map<String, String> BYTES_FIELD = ImmutableMap.of("v", BYTES_BASE64);
3944
private static final Map<String, Object> NULL_FIELD =
4045
ImmutableMap.of("v", Data.nullOf(String.class));
4146
private static final Map<String, Object> REPEATED_FIELD =
@@ -60,6 +65,9 @@ public void testFromPb() {
6065
value = FieldValue.fromPb(TIMESTAMP_FIELD);
6166
assertEquals(FieldValue.Attribute.PRIMITIVE, value.attribute());
6267
assertEquals(42000000, value.timestampValue());
68+
value = FieldValue.fromPb(BYTES_FIELD);
69+
assertEquals(FieldValue.Attribute.PRIMITIVE, value.attribute());
70+
assertArrayEquals(BYTES, value.bytesValue());
6371
value = FieldValue.fromPb(NULL_FIELD);
6472
assertNull(value.value());
6573
value = FieldValue.fromPb(REPEATED_FIELD);
@@ -94,6 +102,10 @@ public void testEquals() {
94102
assertEquals(timestampValue, FieldValue.fromPb(TIMESTAMP_FIELD));
95103
assertEquals(timestampValue.hashCode(), FieldValue.fromPb(TIMESTAMP_FIELD).hashCode());
96104

105+
FieldValue bytesValue = new FieldValue(FieldValue.Attribute.PRIMITIVE, BYTES_BASE64);
106+
assertEquals(bytesValue, FieldValue.fromPb(BYTES_FIELD));
107+
assertEquals(bytesValue.hashCode(), FieldValue.fromPb(BYTES_FIELD).hashCode());
108+
97109
FieldValue nullValue = new FieldValue(FieldValue.Attribute.PRIMITIVE, null);
98110
assertEquals(nullValue, FieldValue.fromPb(NULL_FIELD));
99111
assertEquals(nullValue.hashCode(), FieldValue.fromPb(NULL_FIELD).hashCode());

0 commit comments

Comments
 (0)