diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/BaseTableInfo.java b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/BaseTableInfo.java
new file mode 100644
index 000000000000..8845179b31c5
--- /dev/null
+++ b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/BaseTableInfo.java
@@ -0,0 +1,502 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * 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.gcloud.bigquery;
+
+import static com.google.common.base.MoreObjects.firstNonNull;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.api.client.util.Data;
+import com.google.api.services.bigquery.model.Streamingbuffer;
+import com.google.api.services.bigquery.model.Table;
+import com.google.common.base.Function;
+import com.google.common.base.MoreObjects;
+
+import java.io.Serializable;
+import java.math.BigInteger;
+import java.util.Objects;
+
+/**
+ * Base class for Google BigQuery table information. Use {@link TableInfo} for a simple BigQuery
+ * Table. Use {@link ViewInfo} for a BigQuery View Table. Use {@link ExternalTableInfo} for a
+ * BigQuery Table backed by external data.
+ *
+ * @see Managing Tables
+ */
+public abstract class BaseTableInfo implements Serializable {
+
+ static final Function
FROM_PB_FUNCTION =
+ new Function() {
+ @Override
+ public BaseTableInfo apply(Table pb) {
+ return BaseTableInfo.fromPb(pb);
+ }
+ };
+ static final Function TO_PB_FUNCTION =
+ new Function() {
+ @Override
+ public Table apply(BaseTableInfo tableInfo) {
+ return tableInfo.toPb();
+ }
+ };
+
+ private static final long serialVersionUID = -7679032506430816205L;
+
+ /**
+ * The table type.
+ */
+ public enum Type {
+ /**
+ * A normal BigQuery table.
+ */
+ TABLE,
+ /**
+ * A virtual table defined by a SQL query.
+ *
+ * @see Views
+ */
+ VIEW,
+ /**
+ * A BigQuery table backed by external data.
+ *
+ * @see Federated Data
+ * Sources
+ */
+ EXTERNAL
+ }
+
+ /**
+ * Google BigQuery Table's Streaming Buffer information. This class contains information on a
+ * table's streaming buffer as the estimated size in number of rows/bytes.
+ */
+ public static class StreamingBuffer implements Serializable {
+
+ private static final long serialVersionUID = -6713971364725267597L;
+ private final long estimatedRows;
+ private final long estimatedBytes;
+ private final long oldestEntryTime;
+
+ StreamingBuffer(long estimatedRows, long estimatedBytes, long oldestEntryTime) {
+ this.estimatedRows = estimatedRows;
+ this.estimatedBytes = estimatedBytes;
+ this.oldestEntryTime = oldestEntryTime;
+ }
+
+ /**
+ * Returns a lower-bound estimate of the number of rows currently in the streaming buffer.
+ */
+ public long estimatedRows() {
+ return estimatedRows;
+ }
+
+ /**
+ * Returns a lower-bound estimate of the number of bytes currently in the streaming buffer.
+ */
+ public long estimatedBytes() {
+ return estimatedBytes;
+ }
+
+ /**
+ * Returns the timestamp of the oldest entry in the streaming buffer, in milliseconds since
+ * epoch.
+ */
+ public long oldestEntryTime() {
+ return oldestEntryTime;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("estimatedRows", estimatedRows)
+ .add("estimatedBytes", estimatedBytes)
+ .add("oldestEntryTime", oldestEntryTime)
+ .toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(estimatedRows, estimatedBytes, oldestEntryTime);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof StreamingBuffer
+ && Objects.equals(toPb(), ((StreamingBuffer) obj).toPb());
+ }
+
+ public Streamingbuffer toPb() {
+ return new Streamingbuffer()
+ .setEstimatedBytes(BigInteger.valueOf(estimatedBytes))
+ .setEstimatedRows(BigInteger.valueOf(estimatedRows))
+ .setOldestEntryTime(BigInteger.valueOf(oldestEntryTime));
+ }
+
+ static StreamingBuffer fromPb(Streamingbuffer streamingBufferPb) {
+ return new StreamingBuffer(streamingBufferPb.getEstimatedRows().longValue(),
+ streamingBufferPb.getEstimatedBytes().longValue(),
+ streamingBufferPb.getOldestEntryTime().longValue());
+ }
+ }
+
+ private final String etag;
+ private final String id;
+ private final String selfLink;
+ private final TableId tableId;
+ private final Type type;
+ private final Schema schema;
+ private final String friendlyName;
+ private final String description;
+ private final Long numBytes;
+ private final Long numRows;
+ private final Long creationTime;
+ private final Long expirationTime;
+ private final Long lastModifiedTime;
+
+ public static abstract class Builder> {
+
+ private String etag;
+ private String id;
+ private String selfLink;
+ private TableId tableId;
+ private Type type;
+ private Schema schema;
+ private String friendlyName;
+ private String description;
+ private Long numBytes;
+ private Long numRows;
+ private Long creationTime;
+ private Long expirationTime;
+ private Long lastModifiedTime;
+
+ protected Builder() {}
+
+ protected Builder(BaseTableInfo tableInfo) {
+ this.etag = tableInfo.etag;
+ this.id = tableInfo.id;
+ this.selfLink = tableInfo.selfLink;
+ this.tableId = tableInfo.tableId;
+ this.type = tableInfo.type;
+ this.schema = tableInfo.schema;
+ this.friendlyName = tableInfo.friendlyName;
+ this.description = tableInfo.description;
+ this.numBytes = tableInfo.numBytes;
+ this.numRows = tableInfo.numRows;
+ this.creationTime = tableInfo.creationTime;
+ this.expirationTime = tableInfo.expirationTime;
+ this.lastModifiedTime = tableInfo.lastModifiedTime;
+ }
+
+ protected Builder(Table tablePb) {
+ this.type = Type.valueOf(tablePb.getType());
+ this.tableId = TableId.fromPb(tablePb.getTableReference());
+ if (tablePb.getSchema() != null) {
+ this.schema(Schema.fromPb(tablePb.getSchema()));
+ }
+ if (tablePb.getLastModifiedTime() != null) {
+ this.lastModifiedTime(tablePb.getLastModifiedTime().longValue());
+ }
+ if (tablePb.getNumRows() != null) {
+ this.numRows(tablePb.getNumRows().longValue());
+ }
+ this.description = tablePb.getDescription();
+ this.expirationTime = tablePb.getExpirationTime();
+ this.friendlyName = tablePb.getFriendlyName();
+ this.creationTime = tablePb.getCreationTime();
+ this.etag = tablePb.getEtag();
+ this.id = tablePb.getId();
+ this.numBytes = tablePb.getNumBytes();
+ this.selfLink = tablePb.getSelfLink();
+ }
+
+ @SuppressWarnings("unchecked")
+ protected B self() {
+ return (B) this;
+ }
+
+ B creationTime(Long creationTime) {
+ this.creationTime = creationTime;
+ return self();
+ }
+
+ /**
+ * Sets a user-friendly description for the table.
+ */
+ public B description(String description) {
+ this.description = firstNonNull(description, Data.nullOf(String.class));
+ return self();
+ }
+
+ B etag(String etag) {
+ this.etag = etag;
+ return self();
+ }
+
+ /**
+ * Sets the time when this table expires, in milliseconds since the epoch. If not present, the
+ * table will persist indefinitely. Expired tables will be deleted and their storage reclaimed.
+ */
+ public B expirationTime(Long expirationTime) {
+ this.expirationTime = firstNonNull(expirationTime, Data.nullOf(Long.class));
+ return self();
+ }
+
+ /**
+ * Sets a user-friendly name for the table.
+ */
+ public B friendlyName(String friendlyName) {
+ this.friendlyName = firstNonNull(friendlyName, Data.nullOf(String.class));
+ return self();
+ }
+
+ B id(String id) {
+ this.id = id;
+ return self();
+ }
+
+ B lastModifiedTime(Long lastModifiedTime) {
+ this.lastModifiedTime = lastModifiedTime;
+ return self();
+ }
+
+ B numBytes(Long numBytes) {
+ this.numBytes = numBytes;
+ return self();
+ }
+
+ B numRows(Long numRows) {
+ this.numRows = numRows;
+ return self();
+ }
+
+ B selfLink(String selfLink) {
+ this.selfLink = selfLink;
+ return self();
+ }
+
+ /**
+ * Sets the table identity.
+ */
+ public B tableId(TableId tableId) {
+ this.tableId = checkNotNull(tableId);
+ return self();
+ }
+
+ B type(Type type) {
+ this.type = type;
+ return self();
+ }
+
+ /**
+ * Sets the table schema.
+ */
+ public B schema(Schema schema) {
+ this.schema = checkNotNull(schema);
+ return self();
+ }
+
+ /**
+ * Creates an object.
+ */
+ public abstract T build();
+ }
+
+ protected BaseTableInfo(Builder builder) {
+ this.tableId = checkNotNull(builder.tableId);
+ this.etag = builder.etag;
+ this.id = builder.id;
+ this.selfLink = builder.selfLink;
+ this.friendlyName = builder.friendlyName;
+ this.description = builder.description;
+ this.type = builder.type;
+ this.schema = builder.schema;
+ this.numBytes = builder.numBytes;
+ this.numRows = builder.numRows;
+ this.creationTime = builder.creationTime;
+ this.expirationTime = builder.expirationTime;
+ this.lastModifiedTime = builder.lastModifiedTime;
+ }
+
+ /**
+ * Returns the hash of the table resource.
+ */
+ public String etag() {
+ return etag;
+ }
+
+ /**
+ * Returns an opaque id for the table.
+ */
+ public String id() {
+ return id;
+ }
+
+ /**
+ * Returns the table's type. If this table is simple table the method returns {@link Type#TABLE}.
+ * If this table is an external table this method returns {@link Type#EXTERNAL}. If this table is
+ * a view table this method returns {@link Type#VIEW}.
+ */
+ public Type type() {
+ return type;
+ }
+
+ /**
+ * Returns the table's schema.
+ */
+ public Schema schema() {
+ return schema;
+ }
+
+ /**
+ * Returns an URL that can be used to access the resource again. The returned URL can be used for
+ * get or update requests.
+ */
+ public String selfLink() {
+ return selfLink;
+ }
+
+ /**
+ * Returns the table identity.
+ */
+ public TableId tableId() {
+ return tableId;
+ }
+
+ /**
+ * Returns a user-friendly name for the table.
+ */
+ public String friendlyName() {
+ return Data.isNull(friendlyName) ? null : friendlyName;
+ }
+
+ /**
+ * Returns a user-friendly description for the table.
+ */
+ public String description() {
+ return Data.isNull(description) ? null : description;
+ }
+
+ /**
+ * Returns the size of this table in bytes, excluding any data in the streaming buffer.
+ */
+ public Long numBytes() {
+ return numBytes;
+ }
+
+ /**
+ * Returns the number of rows in this table, excluding any data in the streaming buffer.
+ */
+ public Long numRows() {
+ return numRows;
+ }
+
+ /**
+ * Returns the time when this table was created, in milliseconds since the epoch.
+ */
+ public Long creationTime() {
+ return creationTime;
+ }
+
+ /**
+ * Returns the time when this table expires, in milliseconds since the epoch. If not present, the
+ * table will persist indefinitely. Expired tables will be deleted and their storage reclaimed.
+ */
+ public Long expirationTime() {
+ return Data.isNull(expirationTime) ? null : expirationTime;
+ }
+
+ /**
+ * Returns the time when this table was last modified, in milliseconds since the epoch.
+ */
+ public Long lastModifiedTime() {
+ return lastModifiedTime;
+ }
+
+ /**
+ * Returns a builder for the object.
+ */
+ public abstract Builder toBuilder();
+
+ protected MoreObjects.ToStringHelper toStringHelper() {
+ return MoreObjects.toStringHelper(this)
+ .add("tableId", tableId)
+ .add("type", type)
+ .add("schema", schema)
+ .add("etag", etag)
+ .add("id", id)
+ .add("selfLink", selfLink)
+ .add("friendlyName", friendlyName)
+ .add("description", description)
+ .add("numBytes", numBytes)
+ .add("numRows", numRows)
+ .add("expirationTime", expirationTime)
+ .add("creationTime", creationTime)
+ .add("lastModifiedTime", lastModifiedTime);
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper().toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(tableId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof BaseTableInfo && Objects.equals(toPb(), ((BaseTableInfo) obj).toPb());
+ }
+
+ Table toPb() {
+ Table tablePb = new Table();
+ tablePb.setTableReference(tableId.toPb());
+ if (lastModifiedTime != null) {
+ tablePb.setLastModifiedTime(BigInteger.valueOf(lastModifiedTime));
+ }
+ if (numRows != null) {
+ tablePb.setNumRows(BigInteger.valueOf(numRows));
+ }
+ if (schema != null) {
+ tablePb.setSchema(schema.toPb());
+ }
+ tablePb.setType(type.name());
+ tablePb.setCreationTime(creationTime);
+ tablePb.setDescription(description);
+ tablePb.setEtag(etag);
+ tablePb.setExpirationTime(expirationTime);
+ tablePb.setFriendlyName(friendlyName);
+ tablePb.setId(id);
+ tablePb.setNumBytes(numBytes);
+ tablePb.setSelfLink(selfLink);
+ return tablePb;
+ }
+
+ @SuppressWarnings("unchecked")
+ static T fromPb(Table tablePb) {
+ switch (Type.valueOf(tablePb.getType())) {
+ case TABLE:
+ return (T) TableInfo.fromPb(tablePb);
+ case VIEW:
+ return (T) ViewInfo.fromPb(tablePb);
+ case EXTERNAL:
+ return (T) ExternalTableInfo.fromPb(tablePb);
+ default:
+ // never reached
+ throw new IllegalArgumentException("Format " + tablePb.getType() + " is not supported");
+ }
+ }
+}
diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/CsvOptions.java b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/CsvOptions.java
new file mode 100644
index 000000000000..40655e2c0c36
--- /dev/null
+++ b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/CsvOptions.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * 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.gcloud.bigquery;
+
+import com.google.common.base.MoreObjects;
+
+import java.nio.charset.Charset;
+import java.util.Objects;
+
+/**
+ * Google BigQuery options for CSV format. This class wraps some properties of CSV files used by
+ * BigQuery to parse external data.
+ */
+public class CsvOptions extends FormatOptions {
+
+ private static final long serialVersionUID = 2193570529308612708L;
+
+ private final Boolean allowJaggedRows;
+ private final Boolean allowQuotedNewLines;
+ private final String encoding;
+ private final String fieldDelimiter;
+ private final String quote;
+ private final Integer skipLeadingRows;
+
+ public static final class Builder {
+
+ private Boolean allowJaggedRows;
+ private Boolean allowQuotedNewLines;
+ private String encoding;
+ private String fieldDelimiter;
+ private String quote;
+ private Integer skipLeadingRows;
+
+ private Builder() {}
+
+ /**
+ * Set whether BigQuery should accept rows that are missing trailing optional columns. If
+ * {@code true}, BigQuery treats missing trailing columns as null values. If {@code false},
+ * records with missing trailing columns are treated as bad records, and if there are too many
+ * bad records, an invalid error is returned in the job result. By default, rows with missing
+ * trailing columns are considered bad records.
+ */
+ public Builder allowJaggedRows(Boolean allowJaggedRows) {
+ this.allowJaggedRows = allowJaggedRows;
+ return this;
+ }
+
+ /**
+ * Sets whether BigQuery should allow quoted data sections that contain newline characters in a
+ * CSV file. By default quoted newline are not allowed.
+ */
+ public Builder allowQuotedNewLines(Boolean allowQuotedNewLines) {
+ this.allowQuotedNewLines = allowQuotedNewLines;
+ return this;
+ }
+
+ /**
+ * Sets the character encoding of the data. The supported values are UTF-8 or ISO-8859-1. The
+ * default value is UTF-8. BigQuery decodes the data after the raw, binary data has been split
+ * using the values set in {@link #quote(String)} and {@link #fieldDelimiter(String)}.
+ */
+ public Builder encoding(String encoding) {
+ this.encoding = encoding;
+ return this;
+ }
+
+ /**
+ * Sets the character encoding of the data. The supported values are UTF-8 or ISO-8859-1. The
+ * default value is UTF-8. BigQuery decodes the data after the raw, binary data has been split
+ * using the values set in {@link #quote(String)} and {@link #fieldDelimiter(String)}.
+ */
+ public Builder encoding(Charset encoding) {
+ this.encoding = encoding.name();
+ return this;
+ }
+
+ /**
+ * Sets the separator for fields in a CSV file. BigQuery converts the string to ISO-8859-1
+ * encoding, and then uses the first byte of the encoded string to split the data in its raw,
+ * binary state. BigQuery also supports the escape sequence "\t" to specify a tab separator.
+ * The default value is a comma (',').
+ */
+ public Builder fieldDelimiter(String fieldDelimiter) {
+ this.fieldDelimiter = fieldDelimiter;
+ return this;
+ }
+
+ /**
+ * Sets the value that is used to quote data sections in a CSV file. BigQuery converts the
+ * string to ISO-8859-1 encoding, and then uses the first byte of the encoded string to split
+ * the data in its raw, binary state. The default value is a double-quote ('"'). If your data
+ * does not contain quoted sections, set the property value to an empty string. If your data
+ * contains quoted newline characters, you must also set {@link #allowQuotedNewLines(Boolean)}
+ * property to {@code true}.
+ */
+ public Builder quote(String quote) {
+ this.quote = quote;
+ return this;
+ }
+
+ /**
+ * Sets the number of rows at the top of a CSV file that BigQuery will skip when reading the
+ * data. The default value is 0. This property is useful if you have header rows in the file
+ * that should be skipped.
+ */
+ public Builder skipLeadingRows(Integer skipLeadingRows) {
+ this.skipLeadingRows = skipLeadingRows;
+ return this;
+ }
+
+ /**
+ * Creates a {@code CsvOptions} object.
+ */
+ public CsvOptions build() {
+ return new CsvOptions(this);
+ }
+ }
+
+ private CsvOptions(Builder builder) {
+ super(FormatOptions.CSV);
+ this.allowJaggedRows = builder.allowJaggedRows;
+ this.allowQuotedNewLines = builder.allowQuotedNewLines;
+ this.encoding = builder.encoding;
+ this.fieldDelimiter = builder.fieldDelimiter;
+ this.quote = builder.quote;
+ this.skipLeadingRows = builder.skipLeadingRows;
+ }
+
+ /**
+ * Returns whether BigQuery should accept rows that are missing trailing optional columns. If
+ * {@code true}, BigQuery treats missing trailing columns as null values. If {@code false},
+ * records with missing trailing columns are treated as bad records, and if the number of bad
+ * records exceeds {@link ExternalDataConfiguration#maxBadRecords()}, an invalid error is returned
+ * in the job result.
+ */
+ public Boolean allowJaggedRows() {
+ return allowJaggedRows;
+ }
+
+ /**
+ * Returns whether BigQuery should allow quoted data sections that contain newline characters in a
+ * CSV file.
+ */
+ public Boolean allowQuotedNewLines() {
+ return allowQuotedNewLines;
+ }
+
+ /**
+ * Returns the character encoding of the data. The supported values are UTF-8 or ISO-8859-1. If
+ * not set, UTF-8 is used. BigQuery decodes the data after the raw, binary data has been split
+ * using the values set in {@link #quote()} and {@link #fieldDelimiter()}.
+ */
+ public String encoding() {
+ return encoding;
+ }
+
+ /**
+ * Returns the separator for fields in a CSV file.
+ */
+ public String fieldDelimiter() {
+ return fieldDelimiter;
+ }
+
+ /**
+ * Returns the value that is used to quote data sections in a CSV file.
+ */
+ public String quote() {
+ return quote;
+ }
+
+ /**
+ * Returns the number of rows at the top of a CSV file that BigQuery will skip when reading the
+ * data.
+ */
+ public Integer skipLeadingRows() {
+ return skipLeadingRows;
+ }
+
+ public Builder toBuilder() {
+ return new Builder()
+ .allowJaggedRows(allowJaggedRows)
+ .allowQuotedNewLines(allowQuotedNewLines)
+ .encoding(encoding)
+ .fieldDelimiter(fieldDelimiter)
+ .quote(quote)
+ .skipLeadingRows(skipLeadingRows);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("type", type())
+ .add("allowJaggedRows", allowJaggedRows)
+ .add("allowQuotedNewLines", allowQuotedNewLines)
+ .add("encoding", encoding)
+ .add("fieldDelimiter", fieldDelimiter)
+ .add("quote", quote)
+ .add("skipLeadingRows", skipLeadingRows)
+ .toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(type(), allowJaggedRows, allowQuotedNewLines, encoding, fieldDelimiter,
+ quote, skipLeadingRows);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof CsvOptions && Objects.equals(toPb(), ((CsvOptions) obj).toPb());
+ }
+
+ com.google.api.services.bigquery.model.CsvOptions toPb() {
+ com.google.api.services.bigquery.model.CsvOptions csvOptions =
+ new com.google.api.services.bigquery.model.CsvOptions();
+ csvOptions.setAllowJaggedRows(allowJaggedRows);
+ csvOptions.setAllowQuotedNewlines(allowQuotedNewLines);
+ csvOptions.setEncoding(encoding);
+ csvOptions.setFieldDelimiter(fieldDelimiter);
+ csvOptions.setQuote(quote);
+ csvOptions.setSkipLeadingRows(skipLeadingRows);
+ return csvOptions;
+ }
+
+ /**
+ * Returns a builder for a CsvOptions object.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ static CsvOptions fromPb(com.google.api.services.bigquery.model.CsvOptions csvOptions) {
+ Builder builder = builder();
+ if (csvOptions.getAllowJaggedRows() != null) {
+ builder.allowJaggedRows(csvOptions.getAllowJaggedRows());
+ }
+ if (csvOptions.getAllowQuotedNewlines() != null) {
+ builder.allowQuotedNewLines(csvOptions.getAllowQuotedNewlines());
+ }
+ if (csvOptions.getEncoding() != null) {
+ builder.encoding(csvOptions.getEncoding());
+ }
+ if (csvOptions.getFieldDelimiter() != null) {
+ builder.fieldDelimiter(csvOptions.getFieldDelimiter());
+ }
+ if (csvOptions.getQuote() != null) {
+ builder.quote(csvOptions.getQuote());
+ }
+ if (csvOptions.getSkipLeadingRows() != null) {
+ builder.skipLeadingRows(csvOptions.getSkipLeadingRows());
+ }
+ return builder.build();
+ }
+}
diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/ExternalDataConfiguration.java b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/ExternalDataConfiguration.java
new file mode 100644
index 000000000000..4344aeba186b
--- /dev/null
+++ b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/ExternalDataConfiguration.java
@@ -0,0 +1,397 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * 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.gcloud.bigquery;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Function;
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Google BigQuery configuration for tables backed by external data. Objects of this class describe
+ * the data format, location, and other properties of a table stored outside of BigQuery.
+ * By defining these properties, the data source can then be queried as if it were a standard
+ * BigQuery table. Support for external tables is experimental and might be subject to changes or
+ * removed.
+ *
+ * @see Federated Data Sources
+ *
+ */
+public class ExternalDataConfiguration implements Serializable {
+
+ static final Function FROM_PB_FUNCTION =
+ new Function() {
+ @Override
+ public ExternalDataConfiguration apply(
+ com.google.api.services.bigquery.model.ExternalDataConfiguration configurationPb) {
+ return ExternalDataConfiguration.fromPb(configurationPb);
+ }
+ };
+ static final Function TO_PB_FUNCTION =
+ new Function() {
+ @Override
+ public com.google.api.services.bigquery.model.ExternalDataConfiguration apply(
+ ExternalDataConfiguration configuration) {
+ return configuration.toPb();
+ }
+ };
+
+ private static final long serialVersionUID = -8004288831035566549L;
+
+ private final List sourceUris;
+ private final Schema schema;
+ private final FormatOptions formatOptions;
+ private final Integer maxBadRecords;
+ private final Boolean ignoreUnknownValues;
+ private final String compression;
+
+ public static final class Builder {
+
+ private List sourceUris;
+ private Schema schema;
+ private FormatOptions formatOptions;
+ private Integer maxBadRecords;
+ private Boolean ignoreUnknownValues;
+ private String compression;
+
+ private Builder() {}
+
+ /**
+ * Sets the fully-qualified URIs that point to your data in Google Cloud Storage (e.g.
+ * gs://bucket/path). Each URI can contain one '*' wildcard character that must come after the
+ * bucket's name. Size limits related to load jobs apply to external data sources, plus an
+ * additional limit of 10 GB maximum size across all URIs.
+ *
+ * @see Quota
+ */
+ public Builder sourceUris(List sourceUris) {
+ this.sourceUris = ImmutableList.copyOf(checkNotNull(sourceUris));
+ return this;
+ }
+
+ /**
+ * Sets the schema for the external data.
+ */
+ public Builder schema(Schema schema) {
+ this.schema = checkNotNull(schema);
+ return this;
+ }
+
+ /**
+ * Sets the source format, and possibly some parsing options, of the external data. Supported
+ * formats are {@code CSV} and {@code NEWLINE_DELIMITED_JSON}.
+ *
+ *
+ * Source Format
+ */
+ public Builder formatOptions(FormatOptions formatOptions) {
+ this.formatOptions = checkNotNull(formatOptions);
+ return this;
+ }
+
+ /**
+ * Sets the maximum number of bad records that BigQuery can ignore when reading data. If the
+ * number of bad records exceeds this value, an invalid error is returned in the job result.
+ * The default value is 0, which requires that all records are valid.
+ */
+ public Builder maxBadRecords(Integer maxBadRecords) {
+ this.maxBadRecords = maxBadRecords;
+ return this;
+ }
+
+ /**
+ * Sets whether BigQuery should allow extra values that are not represented in the table schema.
+ * If true, the extra values are ignored. If false, records with extra columns are treated as
+ * bad records, and if there are too many bad records, an invalid error is returned in the job
+ * result. The default value is false. The value set with {@link #formatOptions(FormatOptions)}
+ * property determines what BigQuery treats as an extra value.
+ *
+ * @see
+ * Ignore Unknown Values
+ */
+ public Builder ignoreUnknownValues(Boolean ignoreUnknownValues) {
+ this.ignoreUnknownValues = ignoreUnknownValues;
+ return this;
+ }
+
+ /**
+ * Sets compression type of the data source. By default no compression is assumed.
+ *
+ * @see
+ * Compression
+ */
+ public Builder compression(String compression) {
+ this.compression = compression;
+ return this;
+ }
+
+ /**
+ * Creates an {@code ExternalDataConfiguration} object.
+ */
+ public ExternalDataConfiguration build() {
+ return new ExternalDataConfiguration(this);
+ }
+ }
+
+ ExternalDataConfiguration(Builder builder) {
+ this.compression = builder.compression;
+ this.ignoreUnknownValues = builder.ignoreUnknownValues;
+ this.maxBadRecords = builder.maxBadRecords;
+ this.schema = builder.schema;
+ this.formatOptions = builder.formatOptions;
+ this.sourceUris = builder.sourceUris;
+ }
+
+ /**
+ * Returns the compression type of the data source.
+ *
+ * @see
+ * Compression
+ */
+ public String compression() {
+ return compression;
+ }
+
+ /**
+ * Returns whether BigQuery should allow extra values that are not represented in the table
+ * schema. If true, the extra values are ignored. If false, records with extra columns are treated
+ * as bad records, and if there are too many bad records, an invalid error is returned in the job
+ * result. The default value is false. The value of {@link #formatOptions()} determines what
+ * BigQuery treats as an extra value.
+ *
+ * @see
+ * Ignore Unknown Values
+ */
+ public Boolean ignoreUnknownValues() {
+ return ignoreUnknownValues;
+ }
+
+ /**
+ * Returns the maximum number of bad records that BigQuery can ignore when reading data. If the
+ * number of bad records exceeds this value, an invalid error is returned in the job result.
+ */
+ public Integer maxBadRecords() {
+ return maxBadRecords;
+ }
+
+ /**
+ * Returns the schema for the external data.
+ */
+ public Schema schema() {
+ return schema;
+ }
+
+ /**
+ * Returns the fully-qualified URIs that point to your data in Google Cloud Storage. Each URI can
+ * contain one '*' wildcard character that must come after the bucket's name. Size limits
+ * related to load jobs apply to external data sources, plus an additional limit of 10 GB
+ * maximum size across all URIs.
+ *
+ * @see Quota
+ */
+ public List sourceUris() {
+ return sourceUris;
+ }
+
+ /**
+ * Returns the source format, and possibly some parsing options, of the external data. Supported
+ * formats are {@code CSV} and {@code NEWLINE_DELIMITED_JSON}.
+ */
+ @SuppressWarnings("unchecked")
+ public F formatOptions() {
+ return (F) formatOptions;
+ }
+
+ /**
+ * Returns a builder for the {@code ExternalDataConfiguration} object.
+ */
+ public Builder toBuilder() {
+ return new Builder()
+ .compression(compression)
+ .ignoreUnknownValues(ignoreUnknownValues)
+ .maxBadRecords(maxBadRecords)
+ .schema(schema)
+ .formatOptions(formatOptions)
+ .sourceUris(sourceUris);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("sourceUris", sourceUris)
+ .add("formatOptions", formatOptions)
+ .add("schema", schema)
+ .add("compression", compression)
+ .add("ignoreUnknownValues", ignoreUnknownValues)
+ .add("maxBadRecords", maxBadRecords)
+ .toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(compression, ignoreUnknownValues, maxBadRecords, schema, formatOptions,
+ sourceUris);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof ExternalDataConfiguration
+ && Objects.equals(toPb(), ((ExternalDataConfiguration) obj).toPb());
+ }
+
+ com.google.api.services.bigquery.model.ExternalDataConfiguration toPb() {
+ com.google.api.services.bigquery.model.ExternalDataConfiguration externalConfigurationPb =
+ new com.google.api.services.bigquery.model.ExternalDataConfiguration();
+ if (compression != null) {
+ externalConfigurationPb.setCompression(compression);
+ }
+ if (ignoreUnknownValues != null) {
+ externalConfigurationPb.setIgnoreUnknownValues(ignoreUnknownValues);
+ }
+ if (maxBadRecords != null) {
+ externalConfigurationPb.setMaxBadRecords(maxBadRecords);
+ }
+ if (schema != null) {
+ externalConfigurationPb.setSchema(schema.toPb());
+ }
+ if (formatOptions != null) {
+ externalConfigurationPb.setSourceFormat(formatOptions.type());
+ }
+ if (sourceUris != null) {
+ externalConfigurationPb.setSourceUris(sourceUris);
+ }
+ if (formatOptions != null && FormatOptions.CSV.equals(formatOptions.type())) {
+ externalConfigurationPb.setCsvOptions(((CsvOptions) formatOptions).toPb());
+ }
+ return externalConfigurationPb;
+ }
+
+ /**
+ * Creates a builder for an ExternalDataConfiguration object.
+ *
+ * @param sourceUris the fully-qualified URIs that point to your data in Google Cloud Storage.
+ * Each URI can contain one '*' wildcard character that must come after the bucket's name.
+ * Size limits related to load jobs apply to external data sources, plus an additional limit
+ * of 10 GB maximum size across all URIs.
+ * @param schema the schema for the external data
+ * @param format the source format of the external data
+ * @return a builder for an ExternalDataConfiguration object given source URIs, schema and format
+ *
+ * @see Quota
+ * @see
+ * Source Format
+ */
+ public static Builder builder(List sourceUris, Schema schema, FormatOptions format) {
+ return new Builder().sourceUris(sourceUris).schema(schema).formatOptions(format);
+ }
+
+ /**
+ * Creates a builder for an ExternalDataConfiguration object.
+ *
+ * @param sourceUri a fully-qualified URI that points to your data in Google Cloud Storage. The
+ * URI can contain one '*' wildcard character that must come after the bucket's name. Size
+ * limits related to load jobs apply to external data sources.
+ * @param schema the schema for the external data
+ * @param format the source format of the external data
+ * @return a builder for an ExternalDataConfiguration object given source URI, schema and format
+ *
+ * @see Quota
+ * @see
+ * Source Format
+ */
+ public static Builder builder(String sourceUri, Schema schema, FormatOptions format) {
+ return new Builder()
+ .sourceUris(ImmutableList.of(sourceUri))
+ .schema(schema)
+ .formatOptions(format);
+ }
+
+ /**
+ * Creates an ExternalDataConfiguration object.
+ *
+ * @param sourceUris the fully-qualified URIs that point to your data in Google Cloud Storage.
+ * Each URI can contain one '*' wildcard character that must come after the bucket's name.
+ * Size limits related to load jobs apply to external data sources, plus an additional limit
+ * of 10 GB maximum size across all URIs.
+ * @param schema the schema for the external data
+ * @param format the source format of the external data
+ * @return an ExternalDataConfiguration object given source URIs, schema and format
+ *
+ * @see Quota
+ * @see
+ * Source Format
+ */
+ public static ExternalDataConfiguration of(List sourceUris, Schema schema,
+ FormatOptions format) {
+ return builder(sourceUris, schema, format).build();
+ }
+
+ /**
+ * Creates an ExternalDataConfiguration object.
+ *
+ * @param sourceUri a fully-qualified URI that points to your data in Google Cloud Storage. The
+ * URI can contain one '*' wildcard character that must come after the bucket's name. Size
+ * limits related to load jobs apply to external data sources.
+ * @param schema the schema for the external data
+ * @param format the source format of the external data
+ * @return an ExternalDataConfiguration object given source URIs, schema and format
+ *
+ * @see Quota
+ * @see
+ * Source Format
+ */
+ public static ExternalDataConfiguration of(String sourceUri, Schema schema,
+ FormatOptions format) {
+ return builder(sourceUri, schema, format).build();
+ }
+
+ static ExternalDataConfiguration fromPb(
+ com.google.api.services.bigquery.model.ExternalDataConfiguration externalDataConfiguration) {
+ Builder builder = new Builder();
+ if (externalDataConfiguration.getSourceUris() != null) {
+ builder.sourceUris(externalDataConfiguration.getSourceUris());
+ }
+ if (externalDataConfiguration.getSchema() != null) {
+ builder.schema(Schema.fromPb(externalDataConfiguration.getSchema()));
+ }
+ if (externalDataConfiguration.getSourceFormat() != null) {
+ builder.formatOptions(FormatOptions.of(externalDataConfiguration.getSourceFormat()));
+ }
+ if (externalDataConfiguration.getCompression() != null) {
+ builder.compression(externalDataConfiguration.getCompression());
+ }
+ if (externalDataConfiguration.getIgnoreUnknownValues() != null) {
+ builder.ignoreUnknownValues(externalDataConfiguration.getIgnoreUnknownValues());
+ }
+ if (externalDataConfiguration.getCsvOptions() != null) {
+ builder.formatOptions(CsvOptions.fromPb(externalDataConfiguration.getCsvOptions()));
+ }
+ if (externalDataConfiguration.getMaxBadRecords() != null) {
+ builder.maxBadRecords(externalDataConfiguration.getMaxBadRecords());
+ }
+ return builder.build();
+ }
+}
diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/ExternalTableInfo.java b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/ExternalTableInfo.java
new file mode 100644
index 000000000000..775a0294db77
--- /dev/null
+++ b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/ExternalTableInfo.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * 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.gcloud.bigquery;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.api.services.bigquery.model.Table;
+import com.google.common.base.MoreObjects;
+
+/**
+ * Google BigQuery External Table information. BigQuery's external tables are tables whose data
+ * reside outside of BigQuery but can be queried as normal BigQuery tables. External tables are
+ * experimental and might be subject to change or removed.
+ *
+ * @see Federated Data
+ * Sources
+ */
+public class ExternalTableInfo extends BaseTableInfo {
+
+ private static final long serialVersionUID = -5893406738246214865L;
+
+ private final ExternalDataConfiguration configuration;
+ private final StreamingBuffer streamingBuffer;
+
+ public static final class Builder extends BaseTableInfo.Builder {
+
+ private ExternalDataConfiguration configuration;
+ private StreamingBuffer streamingBuffer;
+
+ private Builder() {}
+
+ private Builder(ExternalTableInfo tableInfo) {
+ super(tableInfo);
+ this.configuration = tableInfo.configuration;
+ this.streamingBuffer = tableInfo.streamingBuffer;
+ }
+
+ protected Builder(Table tablePb) {
+ super(tablePb);
+ if (tablePb.getExternalDataConfiguration() != null) {
+ this.configuration =
+ ExternalDataConfiguration.fromPb(tablePb.getExternalDataConfiguration());
+ }
+ if (tablePb.getStreamingBuffer() != null) {
+ this.streamingBuffer = StreamingBuffer.fromPb(tablePb.getStreamingBuffer());
+ }
+ }
+
+ /**
+ * Sets the data format, location and other properties of a table stored outside of BigQuery.
+ *
+ * @see Federated Data
+ * Sources
+ */
+ public Builder configuration(ExternalDataConfiguration configuration) {
+ this.configuration = checkNotNull(configuration);
+ return self();
+ }
+
+ Builder streamingBuffer(StreamingBuffer streamingBuffer) {
+ this.streamingBuffer = streamingBuffer;
+ return self();
+ }
+
+ /**
+ * Creates a {@code ExternalTableInfo} object.
+ */
+ @Override
+ public ExternalTableInfo build() {
+ return new ExternalTableInfo(this);
+ }
+ }
+
+ private ExternalTableInfo(Builder builder) {
+ super(builder);
+ this.configuration = builder.configuration;
+ this.streamingBuffer = builder.streamingBuffer;
+ }
+
+ /**
+ * Returns the data format, location and other properties of a table stored outside of BigQuery.
+ * This property is experimental and might be subject to change or removed.
+ *
+ * @see Federated Data
+ * Sources
+ */
+ public ExternalDataConfiguration configuration() {
+ return configuration;
+ };
+
+ /**
+ * Returns information on the table's streaming buffer if any exists. Returns {@code null} if no
+ * streaming buffer exists.
+ */
+ public StreamingBuffer streamingBuffer() {
+ return streamingBuffer;
+ }
+
+ /**
+ * Returns a builder for the {@code ExternalTableInfo} object.
+ */
+ @Override
+ public Builder toBuilder() {
+ return new Builder(this);
+ }
+
+ @Override
+ protected MoreObjects.ToStringHelper toStringHelper() {
+ return super.toStringHelper()
+ .add("configuration", configuration)
+ .add("streamingBuffer", streamingBuffer);
+ }
+
+ @Override
+ Table toPb() {
+ Table tablePb = super.toPb();
+ tablePb.setExternalDataConfiguration(configuration.toPb());
+ if (streamingBuffer != null) {
+ tablePb.setStreamingBuffer(streamingBuffer.toPb());
+ }
+ return tablePb;
+ }
+
+ /**
+ * Returns a builder for a BigQuery External Table.
+ *
+ * @param tableId table id
+ * @param configuration data format, location and other properties of an External Table
+ */
+ public static Builder builder(TableId tableId, ExternalDataConfiguration configuration) {
+ return new Builder().tableId(tableId).type(Type.EXTERNAL).configuration(configuration);
+ }
+
+ /**
+ * Returns a BigQuery External Table.
+ *
+ * @param table table id
+ * @param configuration data format, location and other properties of an External Table
+ */
+ public static ExternalTableInfo of(TableId table, ExternalDataConfiguration configuration) {
+ return builder(table, configuration).build();
+ }
+
+ @SuppressWarnings("unchecked")
+ static ExternalTableInfo fromPb(Table tablePb) {
+ return new Builder(tablePb).build();
+ }
+}
diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Field.java b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Field.java
new file mode 100644
index 000000000000..54fdb9f50329
--- /dev/null
+++ b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Field.java
@@ -0,0 +1,375 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * 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.gcloud.bigquery;
+
+import static com.google.common.base.MoreObjects.firstNonNull;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.api.client.util.Data;
+import com.google.api.services.bigquery.model.TableFieldSchema;
+import com.google.common.base.Function;
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Google BigQuery Table field. A table field has a name, a value, a mode and possibly a description.
+ * Supported types are: {@link Type#integer()}, {@link Type#bool()}, {@link Type#string()},
+ * {@link Type#floatingPoint()}, {@link Type#timestamp()} and {@link Type#record(Field...)}. One or
+ * more fields form a table's schema.
+ */
+public class Field implements Serializable {
+
+ static final Function FROM_PB_FUNCTION =
+ new Function() {
+ @Override
+ public Field apply(TableFieldSchema pb) {
+ return Field.fromPb(pb);
+ }
+ };
+ static final Function TO_PB_FUNCTION =
+ new Function() {
+ @Override
+ public TableFieldSchema apply(Field field) {
+ return field.toPb();
+ }
+ };
+
+ private static final long serialVersionUID = -8154262932305199256L;
+
+ /**
+ * Data Types for a BigQuery Table field. This class provides factory methods for all BigQuery
+ * field types. To instantiate a RECORD value the list of sub-fields must be provided.
+ *
+ * @see
+ * Data Types
+ */
+ public static class Type implements Serializable {
+
+ private static final long serialVersionUID = 2841484762609576959L;
+
+ public enum Value {
+ STRING, INTEGER, FLOAT, BOOLEAN, TIMESTAMP, RECORD
+ }
+
+ private final Value value;
+ private final List fields;
+
+ private Type(Value value) {
+ this.value = checkNotNull(value);
+ this.fields = null;
+ }
+
+ private Type(Value value, List fields) {
+ checkArgument(fields.size() > 0, "Record must have at least one field");
+ this.value = value;
+ this.fields = fields;
+ }
+
+ /**
+ * Returns the value identifier.
+ *
+ * @see
+ * Data Types
+ */
+ public Value value() {
+ return value;
+ }
+
+ /**
+ * Returns the list of sub-fields if {@link #value()} is set to {@link Value#RECORD}. Returns
+ * {@code null} otherwise.
+ */
+ public List fields() {
+ return fields;
+ }
+
+ /**
+ * Returns a {@link Value#STRING} field value.
+ */
+ public static Type string() {
+ return new Type(Value.STRING);
+ }
+
+ /**
+ * Returns an {@link Value#INTEGER} field value.
+ */
+ public static Type integer() {
+ return new Type(Value.INTEGER);
+ }
+
+ /**
+ * Returns a {@link Value#FLOAT} field value.
+ */
+ public static Type floatingPoint() {
+ return new Type(Value.FLOAT);
+ }
+
+ /**
+ * Returns a {@link Value#BOOLEAN} field value.
+ */
+ public static Type bool() {
+ return new Type(Value.BOOLEAN);
+ }
+
+ /**
+ * Returns a {@link Value#TIMESTAMP} field value.
+ */
+ public static Type timestamp() {
+ return new Type(Value.TIMESTAMP);
+ }
+
+ /**
+ * Returns a {@link Value#RECORD} field value with associated list of sub-fields.
+ */
+ public static Type record(Field... fields) {
+ return new Type(Value.RECORD, ImmutableList.copyOf(fields));
+ }
+
+ /**
+ * Returns a {@link Value#RECORD} field value with associated list of sub-fields.
+ */
+ public static Type record(List fields) {
+ return new Type(Value.RECORD, ImmutableList.copyOf(checkNotNull(fields)));
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("value", value)
+ .add("fields", fields)
+ .toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(value, fields);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Type)) {
+ return false;
+ }
+ Type other = (Type) obj;
+ return Objects.equals(value, other.value)
+ && Objects.equals(fields, other.fields);
+ }
+ }
+
+ /**
+ * Mode for a BigQuery Table field. {@link Mode#NULLABLE} fields can be set to {@code null},
+ * {@link Mode#REQUIRED} fields must be provided. {@link Mode#REPEATED} fields can contain more
+ * than one value.
+ */
+ public enum Mode {
+ NULLABLE, REQUIRED, REPEATED
+ }
+
+ private final String name;
+ private final Type type;
+ private final String mode;
+ private final String description;
+
+ public static final class Builder {
+
+ private String name;
+ private Type type;
+ private String mode;
+ private String description;
+
+ private Builder() {}
+
+ private Builder(Field field) {
+ this.name = field.name;
+ this.type = field.type;
+ this.mode = field.mode;
+ this.description = field.description;
+ }
+
+ /**
+ * Sets the field name. The name must contain only letters (a-z, A-Z), numbers (0-9), or
+ * underscores (_), and must start with a letter or underscore. The maximum length is 128
+ * characters.
+ */
+ public Builder name(String name) {
+ this.name = checkNotNull(name);
+ return this;
+ }
+
+ /**
+ * Sets the value of the field.
+ *
+ * @see
+ * Data Types
+ */
+ public Builder type(Type type) {
+ this.type = checkNotNull(type);
+ return this;
+ }
+
+ /**
+ * Sets the mode of the field. When not specified {@link Mode#NULLABLE} is used.
+ */
+ public Builder mode(Mode mode) {
+ this.mode = mode != null ? mode.name() : Data.nullOf(String.class);
+ return this;
+ }
+
+ /**
+ * Sets the field description. The maximum length is 16K characters.
+ */
+ public Builder description(String description) {
+ this.description = firstNonNull(description, Data.nullOf(String.class));
+ return this;
+ }
+
+ /**
+ * Creates a {@code Field} object.
+ */
+ public Field build() {
+ return new Field(this);
+ }
+ }
+
+ private Field(Builder builder) {
+ this.name = checkNotNull(builder.name);
+ this.type = checkNotNull(builder.type);
+ this.mode = builder.mode;
+ this.description = builder.description;
+ }
+
+ /**
+ * Returns the field name.
+ */
+ public String name() {
+ return name;
+ }
+
+ /**
+ * Returns the field value.
+ *
+ * @see
+ * Data Types
+ */
+ public Type type() {
+ return type;
+ }
+
+ /**
+ * Returns the field mode. By default {@link Mode#NULLABLE} is used.
+ */
+ public Mode mode() {
+ return mode != null ? Mode.valueOf(mode) : null;
+ }
+
+ /**
+ * Returns the field description.
+ */
+ public String description() {
+ return Data.isNull(description) ? null : description;
+ }
+
+ /**
+ * Returns the list of sub-fields if {@link #type()} is a {@link Type.Value#RECORD}. Returns
+ * {@code null} otherwise.
+ */
+ public List fields() {
+ return type.fields();
+ }
+
+ /**
+ * Returns a builder for the {@code Field} object.
+ */
+ public Builder toBuilder() {
+ return new Builder(this);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("name", name)
+ .add("value", type)
+ .add("mode", mode)
+ .add("description", description)
+ .toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, type, mode, description);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof Field && Objects.equals(toPb(), ((Field) obj).toPb());
+ }
+
+ TableFieldSchema toPb() {
+ TableFieldSchema fieldSchemaPb = new TableFieldSchema();
+ fieldSchemaPb.setName(name);
+ fieldSchemaPb.setType(type.value().name());
+ if (mode != null) {
+ fieldSchemaPb.setMode(mode);
+ }
+ if (description != null) {
+ fieldSchemaPb.setDescription(description);
+ }
+ if (fields() != null) {
+ List fieldsPb = Lists.transform(fields(), TO_PB_FUNCTION);
+ fieldSchemaPb.setFields(fieldsPb);
+ }
+ return fieldSchemaPb;
+ }
+
+ /**
+ * Returns a Field object with given name and value.
+ */
+ public static Field of(String name, Type type) {
+ return builder(name, type).build();
+ }
+
+ /**
+ * Returns a builder for a Field object with given name and value.
+ */
+ public static Builder builder(String name, Type type) {
+ return new Builder().name(name).type(type);
+ }
+
+ static Field fromPb(TableFieldSchema fieldSchemaPb) {
+ Builder fieldBuilder = new Builder();
+ fieldBuilder.name(fieldSchemaPb.getName());
+ Type.Value enumValue = Type.Value.valueOf(fieldSchemaPb.getType());
+ if (fieldSchemaPb.getMode() != null) {
+ fieldBuilder.mode(Mode.valueOf(fieldSchemaPb.getMode()));
+ }
+ if (fieldSchemaPb.getDescription() != null) {
+ fieldBuilder.description(fieldSchemaPb.getDescription());
+ }
+ if (fieldSchemaPb.getFields() != null) {
+ fieldBuilder.type(Type.record(Lists.transform(fieldSchemaPb.getFields(), FROM_PB_FUNCTION)));
+ } else {
+ fieldBuilder.type(new Type(enumValue));
+ }
+ return fieldBuilder.build();
+ }
+}
diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/FormatOptions.java b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/FormatOptions.java
new file mode 100644
index 000000000000..e1f9d5aeb545
--- /dev/null
+++ b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/FormatOptions.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * 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.gcloud.bigquery;
+
+import com.google.common.base.MoreObjects;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * Base class for Google BigQuery format options. These class define the format of external data
+ * used by BigQuery, for either federated tables or load jobs.
+ */
+public class FormatOptions implements Serializable {
+
+ static final String CSV = "CSV";
+ static final String JSON = "NEWLINE_DELIMITED_JSON";
+ static final String DATASTORE_BACKUP = "DATASTORE_BACKUP";
+ private static final long serialVersionUID = -443376052020423691L;
+
+ private final String type;
+
+ FormatOptions(String type) {
+ this.type = type;
+ }
+
+ /**
+ * Returns the external data format, as a string.
+ */
+ public String type() {
+ return type;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this).add("format", type).toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(type);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof FormatOptions && Objects.equals(type, ((FormatOptions) obj).type());
+ }
+
+ /**
+ * Default options for CSV format.
+ */
+ public static CsvOptions csv() {
+ return CsvOptions.builder().build();
+ }
+
+ /**
+ * Default options for NEWLINE_DELIMITED_JSON format.
+ */
+ public static FormatOptions json() {
+ return new FormatOptions(JSON);
+ }
+
+ /**
+ * Default options for DATASTORE_BACKUP format.
+ */
+ public static FormatOptions datastoreBackup() {
+ return new FormatOptions(DATASTORE_BACKUP);
+ }
+
+ /**
+ * Default options for the provided format.
+ */
+ public static FormatOptions of(String format) {
+ return new FormatOptions(format);
+ }
+}
diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Schema.java b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Schema.java
new file mode 100644
index 000000000000..0ac6e1b84ade
--- /dev/null
+++ b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Schema.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * 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.gcloud.bigquery;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.api.services.bigquery.model.TableFieldSchema;
+import com.google.common.base.Function;
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * This class represents the schema for a Google BigQuery Table or data source.
+ */
+public class Schema implements Serializable {
+
+ static final Function
+ FROM_PB_FUNCTION = new Function() {
+ @Override
+ public Schema apply(com.google.api.services.bigquery.model.TableSchema pb) {
+ return Schema.fromPb(pb);
+ }
+ };
+ static final Function
+ TO_PB_FUNCTION = new Function() {
+ @Override
+ public com.google.api.services.bigquery.model.TableSchema apply(Schema schema) {
+ return schema.toPb();
+ }
+ };
+
+ private static final long serialVersionUID = 2007400596384553696L;
+
+ private final List fields;
+
+ public static final class Builder {
+
+ private List fields;
+
+ private Builder() {}
+
+ /**
+ * Adds a field's schema to the table's schema.
+ */
+ public Builder addField(Field field) {
+ if (fields == null) {
+ fields = Lists.newArrayList();
+ }
+ fields.add(checkNotNull(field));
+ return this;
+ }
+
+ /**
+ * Sets table's schema fields.
+ */
+ public Builder fields(Iterable fields) {
+ this.fields = Lists.newArrayList(checkNotNull(fields));
+ return this;
+ }
+
+ /**
+ * Sets table's schema fields.
+ */
+ public Builder fields(Field... fields) {
+ this.fields = Lists.newArrayList(fields);
+ return this;
+ }
+
+ /**
+ * Creates an {@code Schema} object.
+ */
+ public Schema build() {
+ return new Schema(this);
+ }
+ }
+
+ private Schema(Builder builder) {
+ this.fields = builder.fields != null ? ImmutableList.copyOf(builder.fields) :
+ ImmutableList.of();
+ }
+
+ /**
+ * Returns the fields in the current table schema.
+ */
+ public List fields() {
+ return fields;
+ }
+
+ /**
+ * Returns a builder for the {@code Schema} object.
+ */
+ public Builder toBuilder() {
+ return builder().fields(fields);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("fields", fields)
+ .toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(fields);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof Schema && Objects.equals(toPb(), ((Schema) obj).toPb());
+ }
+
+ com.google.api.services.bigquery.model.TableSchema toPb() {
+ com.google.api.services.bigquery.model.TableSchema tableSchemaPb =
+ new com.google.api.services.bigquery.model.TableSchema();
+ if (fields != null) {
+ List fieldsPb = Lists.transform(fields, Field.TO_PB_FUNCTION);
+ tableSchemaPb.setFields(fieldsPb);
+ }
+ return tableSchemaPb;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static Schema of(Iterable fields) {
+ return builder().fields(fields).build();
+ }
+
+ public static Schema of(Field... fields) {
+ return builder().fields(fields).build();
+ }
+
+ static Schema fromPb(com.google.api.services.bigquery.model.TableSchema tableSchemaPb) {
+ return Schema.of(Lists.transform(tableSchemaPb.getFields(), Field.FROM_PB_FUNCTION));
+ }
+}
diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/TableInfo.java b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/TableInfo.java
new file mode 100644
index 000000000000..6594a3f25a67
--- /dev/null
+++ b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/TableInfo.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * 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.gcloud.bigquery;
+
+import com.google.api.services.bigquery.model.Table;
+import com.google.common.base.MoreObjects;
+
+/**
+ * A Google BigQuery Table information. A BigQuery table is a standard, two-dimensional table with
+ * individual records organized in rows, and a data type assigned to each column (also called a
+ * field). Individual fields within a record may contain nested and repeated children fields. Every
+ * table is described by a schema that describes field names, types, and other information.
+ *
+ * @see Managing Tables
+ */
+public class TableInfo extends BaseTableInfo {
+
+ private static final long serialVersionUID = -5910575573063546949L;
+
+ private final String location;
+ private final StreamingBuffer streamingBuffer;
+
+ public static final class Builder extends BaseTableInfo.Builder {
+
+ private String location;
+ private StreamingBuffer streamingBuffer;
+
+ private Builder() {}
+
+ private Builder(TableInfo tableInfo) {
+ super(tableInfo);
+ this.location = tableInfo.location;
+ this.streamingBuffer = tableInfo.streamingBuffer;
+ }
+
+ protected Builder(Table tablePb) {
+ super(tablePb);
+ this.location = tablePb.getLocation();
+ if (tablePb.getStreamingBuffer() != null) {
+ this.streamingBuffer = StreamingBuffer.fromPb(tablePb.getStreamingBuffer());
+ }
+ }
+
+ Builder location(String location) {
+ this.location = location;
+ return self();
+ }
+
+ Builder streamingBuffer(StreamingBuffer streamingBuffer) {
+ this.streamingBuffer = streamingBuffer;
+ return self();
+ }
+
+ /**
+ * Creates a {@code TableInfo} object.
+ */
+ @Override
+ public TableInfo build() {
+ return new TableInfo(this);
+ }
+ }
+
+ private TableInfo(Builder builder) {
+ super(builder);
+ this.location = builder.location;
+ this.streamingBuffer = builder.streamingBuffer;
+ }
+
+ /**
+ * Returns the geographic location where the table should reside. This value is inherited from the
+ * dataset.
+ *
+ * @see
+ * Dataset Location
+ */
+ public String location() {
+ return location;
+ }
+
+ /**
+ * Returns information on the table's streaming buffer if any exists. Returns {@code null} if no
+ * streaming buffer exists.
+ */
+ public StreamingBuffer streamingBuffer() {
+ return streamingBuffer;
+ }
+
+ /**
+ * Returns a builder for a BigQuery Table.
+ *
+ * @param tableId table id
+ * @param schema the schema of the table
+ */
+ public static Builder builder(TableId tableId, Schema schema) {
+ return new Builder().tableId(tableId).type(Type.TABLE).schema(schema);
+ }
+
+ /**
+ * Creates BigQuery table given its type
+ *
+ * @param tableId table id
+ * @param schema the schema of the table
+ */
+ public static BaseTableInfo of(TableId tableId, Schema schema) {
+ return builder(tableId, schema).build();
+ }
+
+ /**
+ * Returns a builder for the {@code TableInfo} object.
+ */
+ @Override
+ public Builder toBuilder() {
+ return new Builder(this);
+ }
+
+ @Override
+ protected MoreObjects.ToStringHelper toStringHelper() {
+ return super.toStringHelper()
+ .add("location", location)
+ .add("streamingBuffer", streamingBuffer);
+ }
+
+ @Override
+ Table toPb() {
+ Table tablePb = super.toPb();
+ tablePb.setLocation(location);
+ if (streamingBuffer != null) {
+ tablePb.setStreamingBuffer(streamingBuffer.toPb());
+ }
+ return tablePb;
+ }
+
+ @SuppressWarnings("unchecked")
+ static TableInfo fromPb(Table tablePb) {
+ return new Builder(tablePb).build();
+ }
+}
diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/UserDefinedFunction.java b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/UserDefinedFunction.java
new file mode 100644
index 000000000000..931c1eaf024a
--- /dev/null
+++ b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/UserDefinedFunction.java
@@ -0,0 +1,150 @@
+package com.google.gcloud.bigquery;
+
+import com.google.api.services.bigquery.model.UserDefinedFunctionResource;
+import com.google.common.base.Function;
+import com.google.common.base.MoreObjects;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * Google BigQuery User Defined Function. BigQuery supports user-defined functions (UDFs) written in
+ * JavaScript. A UDF is similar to the "Map" function in a MapReduce: it takes a single row as input
+ * and produces zero or more rows as output. The output can potentially have a different schema than
+ * the input.
+ *
+ * @see User-Defined Functions
+ */
+public abstract class UserDefinedFunction implements Serializable {
+
+ static final Function FROM_PB_FUNCTION =
+ new Function() {
+ @Override
+ public UserDefinedFunction apply(UserDefinedFunctionResource userDefinedFunctionPb) {
+ return UserDefinedFunction.fromPb(userDefinedFunctionPb);
+ }
+ };
+ static final Function TO_PB_FUNCTION =
+ new Function() {
+ @Override
+ public UserDefinedFunctionResource apply(UserDefinedFunction userDefinedFunction) {
+ return userDefinedFunction.toPb();
+ }
+ };
+
+ private static final long serialVersionUID = 8704260561787440287L;
+
+ /**
+ * Type of user-defined function. User defined functions can be provided inline as code blobs
+ * ({@link #INLINE}) or as a Google Cloud Storage URI ({@link #FROM_URI}).
+ */
+ public enum Type {
+ INLINE,
+ FROM_URI
+ }
+
+ private final Type type;
+ private final String content;
+
+ UserDefinedFunction(Type type, String content) {
+ this.type = type;
+ this.content = content;
+ }
+
+ public Type type() {
+ return type;
+ }
+
+ /**
+ * If {@link #type()} is {@link Type#INLINE} this method returns a code blob. If {@link #type()}
+ * is {@link Type#FROM_URI} the method returns a Google Cloud Storage URI (e.g. gs://bucket/path).
+ */
+ public String content() {
+ return content;
+ }
+
+ /**
+ * A Google Cloud BigQuery user-defined function, as a code blob.
+ */
+ static final class InlineFunction extends UserDefinedFunction {
+
+ private static final long serialVersionUID = 1083672109192091686L;
+
+ InlineFunction(String inlineCode) {
+ super(Type.INLINE, inlineCode);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this).add("inlineCode", content()).toString();
+ }
+
+ @Override
+ public com.google.api.services.bigquery.model.UserDefinedFunctionResource toPb() {
+ return new com.google.api.services.bigquery.model.UserDefinedFunctionResource()
+ .setInlineCode(content());
+ }
+ }
+
+ /**
+ * A Google Cloud BigQuery user-defined function, as an URI to Google Cloud Storage.
+ */
+ static final class UriFunction extends UserDefinedFunction {
+
+ private static final long serialVersionUID = 4660331691852223839L;
+
+ UriFunction(String functionUri) {
+ super(Type.FROM_URI, functionUri);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this).add("functionUri", content()).toString();
+ }
+
+ @Override
+ public com.google.api.services.bigquery.model.UserDefinedFunctionResource toPb() {
+ return new com.google.api.services.bigquery.model.UserDefinedFunctionResource()
+ .setResourceUri(content());
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(type, content);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof UserDefinedFunction
+ && Objects.equals(toPb(), ((UserDefinedFunction) obj).toPb());
+ }
+
+ public abstract com.google.api.services.bigquery.model.UserDefinedFunctionResource toPb();
+
+ /**
+ * Creates a Google Cloud BigQuery user-defined function given a code blob.
+ */
+ public static UserDefinedFunction inline(String functionDefinition) {
+ return new InlineFunction(functionDefinition);
+ }
+
+ /**
+ * Creates a Google Cloud BigQuery user-defined function given a Google Cloud Storage URI (e.g.
+ * gs://bucket/path).
+ */
+ public static UserDefinedFunction fromUri(String functionDefinition) {
+ return new UriFunction(functionDefinition);
+ }
+
+ public static UserDefinedFunction fromPb(
+ com.google.api.services.bigquery.model.UserDefinedFunctionResource pb) {
+ if (pb.getInlineCode() != null) {
+ return new InlineFunction(pb.getInlineCode());
+ }
+ if (pb.getResourceUri() != null) {
+ return new UriFunction(pb.getResourceUri());
+ }
+ throw new IllegalArgumentException("Invalid user-defined function");
+ }
+}
diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/ViewInfo.java b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/ViewInfo.java
new file mode 100644
index 000000000000..01e07663b363
--- /dev/null
+++ b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/ViewInfo.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * 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.gcloud.bigquery;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.api.services.bigquery.model.Table;
+import com.google.api.services.bigquery.model.ViewDefinition;
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+import java.util.List;
+
+/**
+ * Google BigQuery View Table information. BigQuery's views are logical views, not materialized
+ * views, which means that the query that defines the view is re-executed every time the view is
+ * queried.
+ *
+ * @see Views
+ */
+public class ViewInfo extends BaseTableInfo {
+
+ private static final long serialVersionUID = 7567772157817454901L;
+
+ private final String query;
+ private final List userDefinedFunctions;
+
+ public static final class Builder extends BaseTableInfo.Builder {
+
+ private String query;
+ private List userDefinedFunctions;
+
+ private Builder() {}
+
+ private Builder(ViewInfo viewInfo) {
+ super(viewInfo);
+ this.query = viewInfo.query;
+ this.userDefinedFunctions = viewInfo.userDefinedFunctions;
+ }
+
+ protected Builder(Table tablePb) {
+ super(tablePb);
+ ViewDefinition viewPb = tablePb.getView();
+ if (viewPb != null) {
+ this.query = viewPb.getQuery();
+ if (viewPb.getUserDefinedFunctionResources() != null) {
+ this.userDefinedFunctions = Lists.transform(viewPb.getUserDefinedFunctionResources(),
+ UserDefinedFunction.FROM_PB_FUNCTION);
+ }
+ }
+ }
+
+ /**
+ * Sets the query used to create the view.
+ */
+ public Builder query(String query) {
+ this.query = checkNotNull(query);
+ return self();
+ }
+
+ /**
+ * Sets user defined functions that can be used by {@link #query()}.
+ *
+ * @see User-Defined Functions
+ *
+ */
+ public Builder userDefinedFunctions(List userDefinedFunctions) {
+ this.userDefinedFunctions = ImmutableList.copyOf(checkNotNull(userDefinedFunctions));
+ return self();
+ }
+
+ /**
+ * Sets user defined functions that can be used by {@link #query()}.
+ *
+ * @see User-Defined Functions
+ *
+ */
+ public Builder userDefinedFunctions(UserDefinedFunction... userDefinedFunctions) {
+ this.userDefinedFunctions = ImmutableList.copyOf(userDefinedFunctions);
+ return self();
+ }
+
+ /**
+ * Creates a {@code ViewInfo} object.
+ */
+ @Override
+ public ViewInfo build() {
+ return new ViewInfo(this);
+ }
+ }
+
+ private ViewInfo(Builder builder) {
+ super(builder);
+ this.query = builder.query;
+ this.userDefinedFunctions = builder.userDefinedFunctions;
+ }
+
+ /**
+ * Returns the query used to create the view.
+ */
+ public String query() {
+ return query;
+ };
+
+ /**
+ * Returns user defined functions that can be used by {@link #query()}. Returns {@code null} if
+ * not set.
+ *
+ * @see User-Defined Functions
+ *
+ */
+ public List userDefinedFunctions() {
+ return userDefinedFunctions;
+ }
+
+ /**
+ * Returns a builder for the {@code ViewInfo} object.
+ */
+ @Override
+ public Builder toBuilder() {
+ return new Builder(this);
+ }
+
+ @Override
+ protected MoreObjects.ToStringHelper toStringHelper() {
+ return super.toStringHelper()
+ .add("query", query)
+ .add("userDefinedFunctions", userDefinedFunctions);
+ }
+
+ @Override
+ Table toPb() {
+ Table tablePb = super.toPb();
+ ViewDefinition viewDefinition = new ViewDefinition().setQuery(query);
+ if (userDefinedFunctions != null) {
+ viewDefinition.setUserDefinedFunctionResources(Lists.transform(userDefinedFunctions,
+ UserDefinedFunction.TO_PB_FUNCTION));
+ }
+ tablePb.setView(viewDefinition);
+ return tablePb;
+ }
+
+ /**
+ * Returns a builder for a BigQuery View Table.
+ *
+ * @param tableId table id
+ * @param query the query used to generate the table
+ */
+ public static Builder builder(TableId tableId, String query) {
+ return new Builder().tableId(tableId).type(Type.VIEW).query(query);
+ }
+
+ /**
+ * Returns a builder for a BigQuery View Table.
+ *
+ * @param table table id
+ * @param query the query used to generate the table
+ * @param functions user-defined functions that can be used by the query
+ */
+ public static Builder builder(TableId table, String query, List functions) {
+ return new Builder()
+ .tableId(table)
+ .type(Type.VIEW)
+ .userDefinedFunctions(functions)
+ .query(query);
+ }
+
+ /**
+ * Returns a builder for a BigQuery View Table.
+ *
+ * @param table table id
+ * @param query the query used to generate the table
+ * @param functions user-defined functions that can be used by the query
+ */
+ public static Builder builder(TableId table, String query, UserDefinedFunction... functions) {
+ return new Builder()
+ .tableId(table)
+ .type(Type.VIEW)
+ .userDefinedFunctions(functions)
+ .query(query);
+ }
+
+ /**
+ * Creates a BigQuery View given table identity and query.
+ *
+ * @param tableId table id
+ * @param query the query used to generate the table
+ */
+ public static ViewInfo of(TableId tableId, String query) {
+ return builder(tableId, query).build();
+ }
+
+ /**
+ * Creates a BigQuery View given table identity, a query and some user-defined functions.
+ *
+ * @param table table id
+ * @param query the query used to generate the table
+ * @param functions user-defined functions that can be used by the query
+ */
+ public static ViewInfo of(TableId table, String query, List functions) {
+ return builder(table, query, functions).build();
+ }
+
+ /**
+ * Creates a BigQuery View given table identity, a query and some user-defined functions.
+ *
+ * @param table table id
+ * @param query the query used to generate the table
+ * @param functions user-defined functions that can be used by the query
+ */
+ public static ViewInfo of(TableId table, String query, UserDefinedFunction... functions) {
+ return builder(table, query, functions).build();
+ }
+
+ @SuppressWarnings("unchecked")
+ static ViewInfo fromPb(Table tablePb) {
+ return new Builder(tablePb).build();
+ }
+}
diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/CsvOptionsTest.java b/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/CsvOptionsTest.java
new file mode 100644
index 000000000000..371202174431
--- /dev/null
+++ b/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/CsvOptionsTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * 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.gcloud.bigquery;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+public class CsvOptionsTest {
+
+ private static final Boolean ALLOW_JAGGED_ROWS = true;
+ private static final Boolean ALLOW_QUOTED_NEWLINE = true;
+ private static final Charset ENCODING = StandardCharsets.UTF_8;
+ private static final String FIELD_DELIMITER = ",";
+ private static final String QUOTE = "\"";
+ private static final Integer SKIP_LEADING_ROWS = 42;
+ private static final CsvOptions CSV_OPTIONS = CsvOptions.builder()
+ .allowJaggedRows(ALLOW_JAGGED_ROWS)
+ .allowQuotedNewLines(ALLOW_QUOTED_NEWLINE)
+ .encoding(ENCODING)
+ .fieldDelimiter(FIELD_DELIMITER)
+ .quote(QUOTE)
+ .skipLeadingRows(SKIP_LEADING_ROWS)
+ .build();
+
+ @Test
+ public void testToBuilder() {
+ compareCsvOptions(CSV_OPTIONS, CSV_OPTIONS.toBuilder().build());
+ CsvOptions csvOptions = CSV_OPTIONS.toBuilder()
+ .fieldDelimiter(";")
+ .build();
+ assertEquals(";", csvOptions.fieldDelimiter());
+ csvOptions = csvOptions.toBuilder().fieldDelimiter(",").build();
+ compareCsvOptions(CSV_OPTIONS, csvOptions);
+ }
+
+ @Test
+ public void testToBuilderIncomplete() {
+ CsvOptions csvOptions = CsvOptions.builder().fieldDelimiter("|").build();
+ assertEquals(csvOptions, csvOptions.toBuilder().build());
+ }
+
+ @Test
+ public void testBuilder() {
+ assertEquals(FormatOptions.CSV, CSV_OPTIONS.type());
+ assertEquals(ALLOW_JAGGED_ROWS, CSV_OPTIONS.allowJaggedRows());
+ assertEquals(ALLOW_QUOTED_NEWLINE, CSV_OPTIONS.allowQuotedNewLines());
+ assertEquals(ENCODING.name(), CSV_OPTIONS.encoding());
+ assertEquals(FIELD_DELIMITER, CSV_OPTIONS.fieldDelimiter());
+ assertEquals(QUOTE, CSV_OPTIONS.quote());
+ assertEquals(SKIP_LEADING_ROWS, CSV_OPTIONS.skipLeadingRows());
+ }
+
+ @Test
+ public void testToAndFromPb() {
+ compareCsvOptions(CSV_OPTIONS, CsvOptions.fromPb(CSV_OPTIONS.toPb()));
+ CsvOptions csvOptions = CsvOptions.builder().allowJaggedRows(ALLOW_JAGGED_ROWS).build();
+ compareCsvOptions(csvOptions, CsvOptions.fromPb(csvOptions.toPb()));
+ }
+
+ private void compareCsvOptions(CsvOptions expected, CsvOptions value) {
+ assertEquals(expected, value);
+ assertEquals(expected.allowJaggedRows(), value.allowJaggedRows());
+ assertEquals(expected.allowQuotedNewLines(), value.allowQuotedNewLines());
+ assertEquals(expected.encoding(), value.encoding());
+ assertEquals(expected.fieldDelimiter(), value.fieldDelimiter());
+ assertEquals(expected.quote(), value.quote());
+ assertEquals(expected.skipLeadingRows(), value.skipLeadingRows());
+ }
+}
diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/ExternalDataConfigurationTest.java b/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/ExternalDataConfigurationTest.java
new file mode 100644
index 000000000000..f9b7c31e1071
--- /dev/null
+++ b/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/ExternalDataConfigurationTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * 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.gcloud.bigquery;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Test;
+
+import java.util.List;
+
+public class ExternalDataConfigurationTest {
+
+ private static final List SOURCE_URIS = ImmutableList.of("uri1", "uri2");
+ private static final Field FIELD_SCHEMA1 =
+ Field.builder("StringField", Field.Type.string())
+ .mode(Field.Mode.NULLABLE)
+ .description("FieldDescription1")
+ .build();
+ private static final Field FIELD_SCHEMA2 =
+ Field.builder("IntegerField", Field.Type.integer())
+ .mode(Field.Mode.REPEATED)
+ .description("FieldDescription2")
+ .build();
+ private static final Field FIELD_SCHEMA3 =
+ Field.builder("RecordField", Field.Type.record(FIELD_SCHEMA1, FIELD_SCHEMA2))
+ .mode(Field.Mode.REQUIRED)
+ .description("FieldDescription3")
+ .build();
+ private static final Schema TABLE_SCHEMA = Schema.of(FIELD_SCHEMA1, FIELD_SCHEMA2, FIELD_SCHEMA3);
+ private static final Integer MAX_BAD_RECORDS = 42;
+ private static final Boolean IGNORE_UNKNOWN_VALUES = true;
+ private static final String COMPRESSION = "GZIP";
+ private static final CsvOptions CSV_OPTIONS = CsvOptions.builder().build();
+ private static final ExternalDataConfiguration CONFIGURATION = ExternalDataConfiguration
+ .builder(SOURCE_URIS, TABLE_SCHEMA, CSV_OPTIONS)
+ .compression(COMPRESSION)
+ .ignoreUnknownValues(IGNORE_UNKNOWN_VALUES)
+ .maxBadRecords(MAX_BAD_RECORDS)
+ .build();
+
+ @Test
+ public void testToBuilder() {
+ compareConfiguration(CONFIGURATION, CONFIGURATION.toBuilder().build());
+ ExternalDataConfiguration configuration = CONFIGURATION.toBuilder().compression("NONE").build();
+ assertEquals("NONE", configuration.compression());
+ configuration = configuration.toBuilder()
+ .compression(COMPRESSION)
+ .build();
+ compareConfiguration(CONFIGURATION, configuration);
+ }
+
+ @Test
+ public void testToBuilderIncomplete() {
+ ExternalDataConfiguration configuration =
+ ExternalDataConfiguration.of(SOURCE_URIS, TABLE_SCHEMA, FormatOptions.json());
+ assertEquals(configuration, configuration.toBuilder().build());
+ }
+
+ @Test
+ public void testBuilder() {
+ assertEquals(COMPRESSION, CONFIGURATION.compression());
+ assertEquals(CSV_OPTIONS, CONFIGURATION.formatOptions());
+ assertEquals(IGNORE_UNKNOWN_VALUES, CONFIGURATION.ignoreUnknownValues());
+ assertEquals(MAX_BAD_RECORDS, CONFIGURATION.maxBadRecords());
+ assertEquals(TABLE_SCHEMA, CONFIGURATION.schema());
+ assertEquals(SOURCE_URIS, CONFIGURATION.sourceUris());
+ }
+
+ @Test
+ public void testToAndFromPb() {
+ compareConfiguration(CONFIGURATION, ExternalDataConfiguration.fromPb(CONFIGURATION.toPb()));
+ ExternalDataConfiguration configuration =
+ ExternalDataConfiguration.builder(SOURCE_URIS, TABLE_SCHEMA, CSV_OPTIONS).build();
+ compareConfiguration(configuration, ExternalDataConfiguration.fromPb(configuration.toPb()));
+ }
+
+ private void compareConfiguration(ExternalDataConfiguration expected,
+ ExternalDataConfiguration value) {
+ assertEquals(expected, value);
+ assertEquals(expected.compression(), value.compression());
+ assertEquals(expected.formatOptions(), value.formatOptions());
+ assertEquals(expected.ignoreUnknownValues(), value.ignoreUnknownValues());
+ assertEquals(expected.maxBadRecords(), value.maxBadRecords());
+ assertEquals(expected.schema(), value.schema());
+ assertEquals(expected.sourceUris(), value.sourceUris());
+ }
+}
diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/FieldTest.java b/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/FieldTest.java
new file mode 100644
index 000000000000..5f039eaed206
--- /dev/null
+++ b/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/FieldTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * 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.gcloud.bigquery;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Test;
+
+public class FieldTest {
+
+ private static final String FIELD_NAME1 = "StringField";
+ private static final String FIELD_NAME2 = "IntegerField";
+ private static final String FIELD_NAME3 = "RecordField";
+ private static final Field.Type FIELD_TYPE1 = Field.Type.string();
+ private static final Field.Type FIELD_TYPE2 = Field.Type.integer();
+ private static final Field.Mode FIELD_MODE1 = Field.Mode.NULLABLE;
+ private static final Field.Mode FIELD_MODE2 = Field.Mode.REPEATED;
+ private static final Field.Mode FIELD_MODE3 = Field.Mode.REQUIRED;
+ private static final String FIELD_DESCRIPTION1 = "FieldDescription1";
+ private static final String FIELD_DESCRIPTION2 = "FieldDescription2";
+ private static final String FIELD_DESCRIPTION3 = "FieldDescription3";
+ private static final Field FIELD_SCHEMA1 = Field.builder(FIELD_NAME1, FIELD_TYPE1)
+ .mode(FIELD_MODE1)
+ .description(FIELD_DESCRIPTION1)
+ .build();
+ private static final Field FIELD_SCHEMA2 = Field.builder(FIELD_NAME2, FIELD_TYPE2)
+ .mode(FIELD_MODE2)
+ .description(FIELD_DESCRIPTION2)
+ .build();
+ private static final Field.Type FIELD_TYPE3 =
+ Field.Type.record(ImmutableList.of(FIELD_SCHEMA1, FIELD_SCHEMA2));
+ private static final Field FIELD_SCHEMA3 = Field
+ .builder(FIELD_NAME3, FIELD_TYPE3)
+ .mode(FIELD_MODE3)
+ .description(FIELD_DESCRIPTION3)
+ .build();
+
+ @Test
+ public void testToBuilder() {
+ compareFieldSchemas(FIELD_SCHEMA1, FIELD_SCHEMA1.toBuilder().build());
+ compareFieldSchemas(FIELD_SCHEMA2, FIELD_SCHEMA2.toBuilder().build());
+ compareFieldSchemas(FIELD_SCHEMA3, FIELD_SCHEMA3.toBuilder().build());
+ Field field = FIELD_SCHEMA1.toBuilder()
+ .description("New Description")
+ .build();
+ assertEquals("New Description", field.description());
+ field = field.toBuilder().description(FIELD_DESCRIPTION1).build();
+ compareFieldSchemas(FIELD_SCHEMA1, field);
+ }
+
+ @Test
+ public void testToBuilderIncomplete() {
+ Field field = Field.of(FIELD_NAME1, FIELD_TYPE1);
+ compareFieldSchemas(field, field.toBuilder().build());
+ field = Field.of(FIELD_NAME2, FIELD_TYPE3);
+ compareFieldSchemas(field, field.toBuilder().build());
+ }
+
+ @Test
+ public void testBuilder() {
+ assertEquals(FIELD_NAME1, FIELD_SCHEMA1.name());
+ assertEquals(FIELD_TYPE1, FIELD_SCHEMA1.type());
+ assertEquals(FIELD_MODE1, FIELD_SCHEMA1.mode());
+ assertEquals(FIELD_DESCRIPTION1, FIELD_SCHEMA1.description());
+ assertEquals(null, FIELD_SCHEMA1.fields());
+ assertEquals(FIELD_NAME3, FIELD_SCHEMA3.name());
+ assertEquals(FIELD_TYPE3, FIELD_SCHEMA3.type());
+ assertEquals(FIELD_MODE3, FIELD_SCHEMA3.mode());
+ assertEquals(FIELD_DESCRIPTION3, FIELD_SCHEMA3.description());
+ assertEquals(ImmutableList.of(FIELD_SCHEMA1, FIELD_SCHEMA2), FIELD_SCHEMA3.fields());
+ }
+
+ @Test
+ public void testToAndFromPb() {
+ compareFieldSchemas(FIELD_SCHEMA1, Field.fromPb(FIELD_SCHEMA1.toPb()));
+ compareFieldSchemas(FIELD_SCHEMA2, Field.fromPb(FIELD_SCHEMA2.toPb()));
+ compareFieldSchemas(FIELD_SCHEMA3, Field.fromPb(FIELD_SCHEMA3.toPb()));
+ Field field = Field.builder(FIELD_NAME1, FIELD_TYPE1).build();
+ compareFieldSchemas(field, Field.fromPb(field.toPb()));
+ }
+
+ private void compareFieldSchemas(Field expected, Field value) {
+ assertEquals(expected, value);
+ assertEquals(expected.name(), value.name());
+ assertEquals(expected.type(), value.type());
+ assertEquals(expected.mode(), value.mode());
+ assertEquals(expected.description(), value.description());
+ assertEquals(expected.fields(), value.fields());
+ }
+}
diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/FormatOptionsTest.java b/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/FormatOptionsTest.java
new file mode 100644
index 000000000000..df939143156b
--- /dev/null
+++ b/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/FormatOptionsTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * 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.gcloud.bigquery;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+public class FormatOptionsTest {
+
+ @Test
+ public void testConstructor() {
+ FormatOptions options = new FormatOptions(FormatOptions.CSV);
+ assertEquals(FormatOptions.CSV, options.type());
+ options = new FormatOptions(FormatOptions.JSON);
+ assertEquals(FormatOptions.JSON, options.type());
+ options = new FormatOptions(FormatOptions.DATASTORE_BACKUP);
+ assertEquals(FormatOptions.DATASTORE_BACKUP, options.type());
+ }
+
+ @Test
+ public void testFactoryMethods() {
+ assertEquals(FormatOptions.CSV, FormatOptions.csv().type());
+ assertEquals(FormatOptions.JSON, FormatOptions.json().type());
+ assertEquals(FormatOptions.DATASTORE_BACKUP, FormatOptions.datastoreBackup().type());
+ }
+
+ @Test
+ public void testEquals() {
+ assertEquals(FormatOptions.csv(), FormatOptions.csv());
+ assertEquals(FormatOptions.csv().hashCode(), FormatOptions.csv().hashCode());
+ assertEquals(FormatOptions.json(), FormatOptions.json());
+ assertEquals(FormatOptions.json().hashCode(), FormatOptions.json().hashCode());
+ assertEquals(FormatOptions.datastoreBackup(), FormatOptions.datastoreBackup());
+ assertEquals(FormatOptions.datastoreBackup().hashCode(),
+ FormatOptions.datastoreBackup().hashCode());
+ }
+}
diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/SchemaTest.java b/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/SchemaTest.java
new file mode 100644
index 000000000000..d24268d2e7cd
--- /dev/null
+++ b/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/SchemaTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * 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.gcloud.bigquery;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Test;
+
+import java.util.List;
+
+public class SchemaTest {
+
+ private static final Field FIELD_SCHEMA1 =
+ Field.builder("StringField", Field.Type.string())
+ .mode(Field.Mode.NULLABLE)
+ .description("FieldDescription1")
+ .build();
+ private static final Field FIELD_SCHEMA2 =
+ Field.builder("IntegerField", Field.Type.integer())
+ .mode(Field.Mode.REPEATED)
+ .description("FieldDescription2")
+ .build();
+ private static final Field FIELD_SCHEMA3 =
+ Field.builder("RecordField", Field.Type.record(FIELD_SCHEMA1, FIELD_SCHEMA2))
+ .mode(Field.Mode.REQUIRED)
+ .description("FieldDescription3")
+ .build();
+ private static final List FIELDS = ImmutableList.of(FIELD_SCHEMA1, FIELD_SCHEMA2,
+ FIELD_SCHEMA3);
+ private static final Schema TABLE_SCHEMA = Schema.builder().fields(FIELDS).build();
+
+ @Test
+ public void testToBuilder() {
+ compareTableSchema(TABLE_SCHEMA, TABLE_SCHEMA.toBuilder().build());
+ }
+
+ @Test
+ public void testBuilder() {
+ assertEquals(FIELDS, TABLE_SCHEMA.fields());
+ Schema schema = TABLE_SCHEMA.toBuilder()
+ .fields(FIELD_SCHEMA1, FIELD_SCHEMA2)
+ .addField(FIELD_SCHEMA3)
+ .build();
+ compareTableSchema(TABLE_SCHEMA, schema);
+ }
+
+ @Test
+ public void testOf() {
+ compareTableSchema(TABLE_SCHEMA, Schema.of(FIELDS));
+ }
+
+ @Test
+ public void testToAndFromPb() {
+ compareTableSchema(TABLE_SCHEMA, Schema.fromPb(TABLE_SCHEMA.toPb()));
+ }
+
+ private void compareTableSchema(Schema expected, Schema value) {
+ assertEquals(expected, value);
+ assertEquals(expected.fields(), value.fields());
+ }
+}
diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/SerializationTest.java b/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/SerializationTest.java
index 93165724e11f..c75337431580 100644
--- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/SerializationTest.java
+++ b/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/SerializationTest.java
@@ -31,6 +31,7 @@
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
+import java.nio.charset.StandardCharsets;
import java.util.List;
public class SerializationTest {
@@ -67,6 +68,66 @@ public class SerializationTest {
.selfLink(SELF_LINK)
.build();
private static final TableId TABLE_ID = TableId.of("project", "dataset", "table");
+ private static final CsvOptions CSV_OPTIONS = CsvOptions.builder()
+ .allowJaggedRows(true)
+ .allowQuotedNewLines(false)
+ .encoding(StandardCharsets.ISO_8859_1)
+ .fieldDelimiter(",")
+ .quote("\"")
+ .skipLeadingRows(42)
+ .build();
+ private static final Field FIELD_SCHEMA1 =
+ Field.builder("StringField", Field.Type.string())
+ .mode(Field.Mode.NULLABLE)
+ .description("FieldDescription1")
+ .build();
+ private static final Field FIELD_SCHEMA2 =
+ Field.builder("IntegerField", Field.Type.integer())
+ .mode(Field.Mode.REPEATED)
+ .description("FieldDescription2")
+ .build();
+ private static final Field FIELD_SCHEMA3 =
+ Field.builder("RecordField", Field.Type.record(FIELD_SCHEMA1, FIELD_SCHEMA2))
+ .mode(Field.Mode.REQUIRED)
+ .description("FieldDescription3")
+ .build();
+ private static final Schema TABLE_SCHEMA = Schema.of(FIELD_SCHEMA1, FIELD_SCHEMA2, FIELD_SCHEMA3);
+ private static final BaseTableInfo.StreamingBuffer STREAMING_BUFFER =
+ new BaseTableInfo.StreamingBuffer(1L, 2L, 3L);
+ private static final List SOURCE_URIS = ImmutableList.of("uri1", "uri2");
+ private static final ExternalDataConfiguration EXTERNAL_DATA_CONFIGURATION =
+ ExternalDataConfiguration.builder(SOURCE_URIS, TABLE_SCHEMA, CSV_OPTIONS)
+ .ignoreUnknownValues(true)
+ .maxBadRecords(42)
+ .build();
+ private static final UserDefinedFunction INLINE_FUNCTION =
+ new UserDefinedFunction.InlineFunction("inline");
+ private static final UserDefinedFunction URI_FUNCTION =
+ new UserDefinedFunction.UriFunction("URI");
+ private static final BaseTableInfo TABLE_INFO =
+ TableInfo.builder(TABLE_ID, TABLE_SCHEMA)
+ .creationTime(CREATION_TIME)
+ .description(DESCRIPTION)
+ .etag(ETAG)
+ .id(ID)
+ .location(LOCATION)
+ .streamingBuffer(STREAMING_BUFFER)
+ .build();
+ private static final ViewInfo VIEW_INFO =
+ ViewInfo.builder(TABLE_ID, "QUERY")
+ .creationTime(CREATION_TIME)
+ .description(DESCRIPTION)
+ .etag(ETAG)
+ .id(ID)
+ .build();
+ private static final ExternalTableInfo EXTERNAL_TABLE_INFO =
+ ExternalTableInfo.builder(TABLE_ID, EXTERNAL_DATA_CONFIGURATION)
+ .creationTime(CREATION_TIME)
+ .description(DESCRIPTION)
+ .etag(ETAG)
+ .id(ID)
+ .streamingBuffer(STREAMING_BUFFER)
+ .build();
@Test
public void testServiceOptions() throws Exception {
@@ -89,7 +150,8 @@ public void testServiceOptions() throws Exception {
@Test
public void testModelAndRequests() throws Exception {
Serializable[] objects = {DOMAIN_ACCESS, GROUP_ACCESS, USER_ACCESS, VIEW_ACCESS, DATASET_ID,
- DATASET_INFO, TABLE_ID};
+ DATASET_INFO, TABLE_ID, CSV_OPTIONS, STREAMING_BUFFER, EXTERNAL_DATA_CONFIGURATION,
+ TABLE_SCHEMA, TABLE_INFO, VIEW_INFO, EXTERNAL_TABLE_INFO, INLINE_FUNCTION, URI_FUNCTION};
for (Serializable obj : objects) {
Object copy = serializeAndDeserialize(obj);
assertEquals(obj, obj);
diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/TableInfoTest.java b/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/TableInfoTest.java
new file mode 100644
index 000000000000..f9e073906578
--- /dev/null
+++ b/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/TableInfoTest.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * 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.gcloud.bigquery;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gcloud.bigquery.BaseTableInfo.StreamingBuffer;
+
+import org.junit.Test;
+
+import java.util.List;
+
+public class TableInfoTest {
+
+ private static final Field FIELD_SCHEMA1 =
+ Field.builder("StringField", Field.Type.string())
+ .mode(Field.Mode.NULLABLE)
+ .description("FieldDescription1")
+ .build();
+ private static final Field FIELD_SCHEMA2 =
+ Field.builder("IntegerField", Field.Type.integer())
+ .mode(Field.Mode.REPEATED)
+ .description("FieldDescription2")
+ .build();
+ private static final Field FIELD_SCHEMA3 =
+ Field.builder("RecordField", Field.Type.record(FIELD_SCHEMA1, FIELD_SCHEMA2))
+ .mode(Field.Mode.REQUIRED)
+ .description("FieldDescription3")
+ .build();
+ private static final Schema TABLE_SCHEMA = Schema.of(FIELD_SCHEMA1, FIELD_SCHEMA2, FIELD_SCHEMA3);
+ private static final String VIEW_QUERY = "VIEW QUERY";
+ private static final List SOURCE_URIS = ImmutableList.of("uri1", "uri2");
+ private static final String SOURCE_FORMAT = "CSV";
+ private static final Integer MAX_BAD_RECORDS = 42;
+ private static final Boolean IGNORE_UNKNOWN_VALUES = true;
+ private static final String COMPRESSION = "GZIP";
+ private static final ExternalDataConfiguration CONFIGURATION = ExternalDataConfiguration
+ .builder(SOURCE_URIS, TABLE_SCHEMA, FormatOptions.datastoreBackup())
+ .compression(COMPRESSION)
+ .ignoreUnknownValues(IGNORE_UNKNOWN_VALUES)
+ .maxBadRecords(MAX_BAD_RECORDS)
+ .build();
+ private static final String ETAG = "etag";
+ private static final String ID = "project:dataset:table";
+ private static final String SELF_LINK = "selfLink";
+ private static final TableId TABLE_ID = TableId.of("dataset", "table");
+ private static final String FRIENDLY_NAME = "friendlyName";
+ private static final String DESCRIPTION = "description";
+ private static final Long NUM_BYTES = 42L;
+ private static final Long NUM_ROWS = 43L;
+ private static final Long CREATION_TIME = 10L;
+ private static final Long EXPIRATION_TIME = 100L;
+ private static final Long LAST_MODIFIED_TIME = 20L;
+ private static final String LOCATION = "US";
+ private static final StreamingBuffer STREAMING_BUFFER = new StreamingBuffer(1L, 2L, 3L);
+ private static final TableInfo TABLE_INFO =
+ TableInfo.builder(TABLE_ID, TABLE_SCHEMA)
+ .creationTime(CREATION_TIME)
+ .description(DESCRIPTION)
+ .etag(ETAG)
+ .expirationTime(EXPIRATION_TIME)
+ .friendlyName(FRIENDLY_NAME)
+ .id(ID)
+ .lastModifiedTime(LAST_MODIFIED_TIME)
+ .location(LOCATION)
+ .numBytes(NUM_BYTES)
+ .numRows(NUM_ROWS)
+ .selfLink(SELF_LINK)
+ .streamingBuffer(STREAMING_BUFFER)
+ .build();
+ private static final ExternalTableInfo EXTERNAL_TABLE_INFO =
+ ExternalTableInfo.builder(TABLE_ID, CONFIGURATION)
+ .creationTime(CREATION_TIME)
+ .description(DESCRIPTION)
+ .etag(ETAG)
+ .expirationTime(EXPIRATION_TIME)
+ .friendlyName(FRIENDLY_NAME)
+ .id(ID)
+ .lastModifiedTime(LAST_MODIFIED_TIME)
+ .numBytes(NUM_BYTES)
+ .numRows(NUM_ROWS)
+ .selfLink(SELF_LINK)
+ .streamingBuffer(STREAMING_BUFFER)
+ .build();
+ private static List USER_DEFINED_FUNCTIONS = ImmutableList.of(
+ UserDefinedFunction.inline("Function"), UserDefinedFunction.fromUri("URI"));
+ private static final ViewInfo VIEW_INFO =
+ ViewInfo.builder(TABLE_ID, VIEW_QUERY, USER_DEFINED_FUNCTIONS)
+ .creationTime(CREATION_TIME)
+ .description(DESCRIPTION)
+ .etag(ETAG)
+ .expirationTime(EXPIRATION_TIME)
+ .friendlyName(FRIENDLY_NAME)
+ .id(ID)
+ .lastModifiedTime(LAST_MODIFIED_TIME)
+ .numBytes(NUM_BYTES)
+ .numRows(NUM_ROWS)
+ .selfLink(SELF_LINK)
+ .build();
+
+ @Test
+ public void testToBuilder() {
+ compareTableInfo(TABLE_INFO, TABLE_INFO.toBuilder().build());
+ compareViewInfo(VIEW_INFO, VIEW_INFO.toBuilder().build());
+ compareExternalTableInfo(EXTERNAL_TABLE_INFO, EXTERNAL_TABLE_INFO.toBuilder().build());
+ BaseTableInfo tableInfo = TABLE_INFO.toBuilder()
+ .description("newDescription")
+ .build();
+ assertEquals("newDescription", tableInfo.description());
+ tableInfo = tableInfo.toBuilder()
+ .description("description")
+ .build();
+ compareBaseTableInfo(TABLE_INFO, tableInfo);
+ }
+
+ @Test
+ public void testToBuilderIncomplete() {
+ BaseTableInfo tableInfo = TableInfo.of(TABLE_ID, TABLE_SCHEMA);
+ assertEquals(tableInfo, tableInfo.toBuilder().build());
+ tableInfo = ViewInfo.of(TABLE_ID, VIEW_QUERY);
+ assertEquals(tableInfo, tableInfo.toBuilder().build());
+ tableInfo = ExternalTableInfo.of(TABLE_ID, CONFIGURATION);
+ assertEquals(tableInfo, tableInfo.toBuilder().build());
+ }
+
+ @Test
+ public void testBuilder() {
+ assertEquals(TABLE_ID, TABLE_INFO.tableId());
+ assertEquals(TABLE_SCHEMA, TABLE_INFO.schema());
+ assertEquals(CREATION_TIME, TABLE_INFO.creationTime());
+ assertEquals(DESCRIPTION, TABLE_INFO.description());
+ assertEquals(ETAG, TABLE_INFO.etag());
+ assertEquals(EXPIRATION_TIME, TABLE_INFO.expirationTime());
+ assertEquals(FRIENDLY_NAME, TABLE_INFO.friendlyName());
+ assertEquals(ID, TABLE_INFO.id());
+ assertEquals(LAST_MODIFIED_TIME, TABLE_INFO.lastModifiedTime());
+ assertEquals(LOCATION, TABLE_INFO.location());
+ assertEquals(NUM_BYTES, TABLE_INFO.numBytes());
+ assertEquals(NUM_ROWS, TABLE_INFO.numRows());
+ assertEquals(SELF_LINK, TABLE_INFO.selfLink());
+ assertEquals(STREAMING_BUFFER, TABLE_INFO.streamingBuffer());
+ assertEquals(BaseTableInfo.Type.TABLE, TABLE_INFO.type());
+ assertEquals(TABLE_ID, VIEW_INFO.tableId());
+ assertEquals(null, VIEW_INFO.schema());
+ assertEquals(VIEW_QUERY, VIEW_INFO.query());
+ assertEquals(BaseTableInfo.Type.VIEW, VIEW_INFO.type());
+ assertEquals(CREATION_TIME, VIEW_INFO.creationTime());
+ assertEquals(DESCRIPTION, VIEW_INFO.description());
+ assertEquals(ETAG, VIEW_INFO.etag());
+ assertEquals(EXPIRATION_TIME, VIEW_INFO.expirationTime());
+ assertEquals(FRIENDLY_NAME, VIEW_INFO.friendlyName());
+ assertEquals(ID, VIEW_INFO.id());
+ assertEquals(LAST_MODIFIED_TIME, VIEW_INFO.lastModifiedTime());
+ assertEquals(NUM_BYTES, VIEW_INFO.numBytes());
+ assertEquals(NUM_ROWS, VIEW_INFO.numRows());
+ assertEquals(SELF_LINK, VIEW_INFO.selfLink());
+ assertEquals(BaseTableInfo.Type.VIEW, VIEW_INFO.type());
+ assertEquals(TABLE_ID, EXTERNAL_TABLE_INFO.tableId());
+ assertEquals(null, EXTERNAL_TABLE_INFO.schema());
+ assertEquals(CONFIGURATION, EXTERNAL_TABLE_INFO.configuration());
+ assertEquals(CREATION_TIME, EXTERNAL_TABLE_INFO.creationTime());
+ assertEquals(DESCRIPTION, EXTERNAL_TABLE_INFO.description());
+ assertEquals(ETAG, EXTERNAL_TABLE_INFO.etag());
+ assertEquals(EXPIRATION_TIME, EXTERNAL_TABLE_INFO.expirationTime());
+ assertEquals(FRIENDLY_NAME, EXTERNAL_TABLE_INFO.friendlyName());
+ assertEquals(ID, EXTERNAL_TABLE_INFO.id());
+ assertEquals(LAST_MODIFIED_TIME, EXTERNAL_TABLE_INFO.lastModifiedTime());
+ assertEquals(NUM_BYTES, EXTERNAL_TABLE_INFO.numBytes());
+ assertEquals(NUM_ROWS, EXTERNAL_TABLE_INFO.numRows());
+ assertEquals(SELF_LINK, EXTERNAL_TABLE_INFO.selfLink());
+ assertEquals(STREAMING_BUFFER, EXTERNAL_TABLE_INFO.streamingBuffer());
+ assertEquals(BaseTableInfo.Type.EXTERNAL, EXTERNAL_TABLE_INFO.type());
+ }
+
+ @Test
+ public void testToAndFromPb() {
+ assertTrue(BaseTableInfo.fromPb(TABLE_INFO.toPb()) instanceof BaseTableInfo);
+ compareTableInfo(TABLE_INFO, BaseTableInfo.fromPb(TABLE_INFO.toPb()));
+ assertTrue(BaseTableInfo.fromPb(VIEW_INFO.toPb()) instanceof ViewInfo);
+ compareViewInfo(VIEW_INFO, BaseTableInfo.fromPb(VIEW_INFO.toPb()));
+ assertTrue(BaseTableInfo.fromPb(EXTERNAL_TABLE_INFO.toPb()) instanceof ExternalTableInfo);
+ compareExternalTableInfo(EXTERNAL_TABLE_INFO,
+ BaseTableInfo.fromPb(EXTERNAL_TABLE_INFO.toPb()));
+ }
+
+ private void compareBaseTableInfo(BaseTableInfo expected, BaseTableInfo value) {
+ assertEquals(expected, value);
+ assertEquals(expected.tableId(), value.tableId());
+ assertEquals(expected.schema(), value.schema());
+ assertEquals(expected.type(), value.type());
+ assertEquals(expected.creationTime(), value.creationTime());
+ assertEquals(expected.description(), value.description());
+ assertEquals(expected.etag(), value.etag());
+ assertEquals(expected.expirationTime(), value.expirationTime());
+ assertEquals(expected.friendlyName(), value.friendlyName());
+ assertEquals(expected.id(), value.id());
+ assertEquals(expected.lastModifiedTime(), value.lastModifiedTime());
+ assertEquals(expected.numBytes(), value.numBytes());
+ assertEquals(expected.numRows(), value.numRows());
+ assertEquals(expected.selfLink(), value.selfLink());
+ assertEquals(expected.type(), value.type());
+ }
+
+ private void compareTableInfo(TableInfo expected, TableInfo value) {
+ compareBaseTableInfo(expected, value);
+ assertEquals(expected, value);
+ assertEquals(expected.location(), value.location());
+ assertEquals(expected.streamingBuffer(), value.streamingBuffer());
+ }
+
+ private void compareViewInfo(ViewInfo expected, ViewInfo value) {
+ compareBaseTableInfo(expected, value);
+ assertEquals(expected, value);
+ assertEquals(expected.query(), value.query());
+ assertEquals(expected.userDefinedFunctions(), value.userDefinedFunctions());
+ }
+
+ private void compareExternalTableInfo(ExternalTableInfo expected, ExternalTableInfo value) {
+ compareBaseTableInfo(expected, value);
+ assertEquals(expected, value);
+ assertEquals(expected.configuration(), value.configuration());
+ assertEquals(expected.streamingBuffer(), value.streamingBuffer());
+ }
+}
diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/UserDefinedFunctionTest.java b/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/UserDefinedFunctionTest.java
new file mode 100644
index 000000000000..2741aaed89a5
--- /dev/null
+++ b/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/UserDefinedFunctionTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * 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.gcloud.bigquery;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+public class UserDefinedFunctionTest {
+
+ private static final String INLINE = "inline";
+ private static final String URI = "uri";
+ private static final UserDefinedFunction INLINE_FUNCTION =
+ new UserDefinedFunction.InlineFunction(INLINE);
+ private static final UserDefinedFunction URI_FUNCTION = new UserDefinedFunction.UriFunction(URI);
+
+ @Test
+ public void testConstructor() {
+ assertEquals(INLINE, INLINE_FUNCTION.content());
+ assertEquals(UserDefinedFunction.Type.INLINE, INLINE_FUNCTION.type());
+ assertEquals(URI, URI_FUNCTION.content());
+ assertEquals(UserDefinedFunction.Type.FROM_URI, URI_FUNCTION.type());
+ }
+
+ @Test
+ public void testFactoryMethod() {
+ compareUserDefinedFunction(INLINE_FUNCTION, UserDefinedFunction.inline(INLINE));
+ compareUserDefinedFunction(URI_FUNCTION, UserDefinedFunction.fromUri(URI));
+ }
+
+ @Test
+ public void testToAndFromPb() {
+ compareUserDefinedFunction(INLINE_FUNCTION, UserDefinedFunction.fromPb(INLINE_FUNCTION.toPb()));
+ compareUserDefinedFunction(URI_FUNCTION, UserDefinedFunction.fromPb(URI_FUNCTION.toPb()));
+ }
+
+ private void compareUserDefinedFunction(UserDefinedFunction expected, UserDefinedFunction value) {
+ assertEquals(expected, value);
+ assertEquals(expected.type(), value.type());
+ assertEquals(expected.content(), value.content());
+ }
+}