rows) {
+ this.rows = Lists.newLinkedList(checkNotNull(rows));
+ return this;
+ }
+
+ /**
+ * Adds a row to be inserted.
+ */
+ public Builder addRow(RowToInsert rowToInsert) {
+ checkNotNull(rowToInsert);
+ if (rows == null) {
+ rows = Lists.newArrayList();
+ }
+ rows.add(rowToInsert);
+ return this;
+ }
+
+ /**
+ * Adds a row to be inserted with associated id.
+ *
+ * Example usage of adding a row with associated id:
+ *
{@code
+ * InsertAllRequest.Builder builder = InsertAllRequest.builder(tableId);
+ * List repeatedFieldValue = Arrays.asList(1L, 2L);
+ * Map recordContent = new HashMap();
+ * recordContent.put("subfieldName1", "value");
+ * recordContent.put("subfieldName2", repeatedFieldValue);
+ * Map rowContent = new HashMap();
+ * rowContent.put("fieldName1", true);
+ * rowContent.put("fieldName2", recordContent);
+ * builder.addRow("rowId", rowContent);
+ * }
+ */
+ public Builder addRow(String id, Map content) {
+ addRow(new RowToInsert(id, content));
+ return this;
+ }
+
+ /**
+ * Adds a row to be inserted without an associated id.
+ *
+ * Example usage of adding a row without an associated id:
+ *
{@code
+ * InsertAllRequest.Builder builder = InsertAllRequest.builder(tableId);
+ * List repeatedFieldValue = Arrays.asList(1L, 2L);
+ * Map recordContent = new HashMap();
+ * recordContent.put("subfieldName1", "value");
+ * recordContent.put("subfieldName2", repeatedFieldValue);
+ * Map rowContent = new HashMap();
+ * rowContent.put("fieldName1", true);
+ * rowContent.put("fieldName2", recordContent);
+ * builder.addRow(rowContent);
+ * }
+ */
+ public Builder addRow(Map content) {
+ addRow(new RowToInsert(null, content));
+ return this;
+ }
+
+ /**
+ * Sets whether to insert all valid rows of a request, even if invalid rows exist. If not set
+ * the entire insert request will fail if it contains an invalid row.
+ */
+ public Builder skipInvalidRows(boolean skipInvalidRows) {
+ this.skipInvalidRows = skipInvalidRows;
+ return this;
+ }
+
+ /**
+ * Sets whether to accept rows that contain values that do not match the schema. The unknown
+ * values are ignored. If not set, rows with unknown values are considered to be invalid.
+ */
+ public Builder ignoreUnknownValues(boolean ignoreUnknownValues) {
+ this.ignoreUnknownValues = ignoreUnknownValues;
+ return this;
+ }
+
+ /**
+ * If specified, the destination table is treated as a base template. Rows are inserted into an
+ * instance table named "{destination}{templateSuffix}". BigQuery will manage the creation of
+ * the instance table, using the schema of the base template table. Table creation might take
+ * some time. To obtain table's information after {@link BigQuery#insertAll(InsertAllRequest)}
+ * is called use:
+ * {@code
+ * String suffixTableId = ...;
+ * TableInfo suffixTable = bigquery.getTable(DATASET, suffixTableId);
+ * while (suffixTable == null) {
+ * Thread.sleep(1000L);
+ * suffixTable = bigquery.getTable(DATASET, suffixTableId);
+ * }}
+ *
+ * @see
+ * Template Tables
+ */
+ public Builder templateSuffix(String templateSuffix) {
+ this.templateSuffix = templateSuffix;
+ return this;
+ }
+
+ public InsertAllRequest build() {
+ return new InsertAllRequest(this);
+ }
+ }
+
+ private InsertAllRequest(Builder builder) {
+ this.table = checkNotNull(builder.table);
+ this.rows = ImmutableList.copyOf(checkNotNull(builder.rows));
+ this.ignoreUnknownValues = builder.ignoreUnknownValues;
+ this.skipInvalidRows = builder.skipInvalidRows;
+ this.templateSuffix = builder.templateSuffix;
+ }
+
+ /**
+ * Returns the destination table for rows insert request.
+ */
+ public TableId table() {
+ return table;
+ }
+
+ /**
+ * Returns the rows to be inserted.
+ */
+ public List rows() {
+ return rows;
+ }
+
+ /**
+ * Returns whether to accept rows that contain values that do not match the schema. The unknown
+ * values are ignored. If not set, rows with unknown values are considered to be invalid.
+ */
+ public Boolean ignoreUnknownValues() {
+ return ignoreUnknownValues;
+ }
+
+ /**
+ * Returns whether to insert all valid rows of a request, even if invalid rows exist. If not set
+ * the entire insert request will fail if it contains an invalid row.
+ */
+ public Boolean skipInvalidRows() {
+ return skipInvalidRows;
+ }
+
+ /**
+ * If specified, the destination table is treated as a base template. Rows are inserted into an
+ * instance table named "{destination}{templateSuffix}". BigQuery will manage the creation of the
+ * instance table, using the schema of the base template table. Table creation might take some
+ * time. To obtain table's information after {@link BigQuery#insertAll(InsertAllRequest)} is
+ * called use:
+ * {@code
+ * String suffixTableId = ...;
+ * TableInfo suffixTable = bigquery.getTable(DATASET, suffixTableId);
+ * while (suffixTable == null) {
+ * Thread.sleep(1000L);
+ * suffixTable = bigquery.getTable(DATASET, suffixTableId);
+ * }}
+ *
+ * @see
+ * Template Tables
+ */
+ public String templateSuffix() {
+ return templateSuffix;
+ }
+
+ /**
+ * Returns a builder for an {@code InsertAllRequest} object given the destination table.
+ */
+ public static Builder builder(TableId table) {
+ return new Builder().table(table);
+ }
+
+ /**
+ * Returns a builder for an {@code InsertAllRequest} object given the destination table and the
+ * rows to insert.
+ */
+ public static Builder builder(TableId table, Iterable rows) {
+ return builder(table).rows(rows);
+ }
+
+ /**
+ * Returns a builder for an {@code InsertAllRequest} object given the destination table and the
+ * rows to insert.
+ */
+ public static Builder builder(TableId table, RowToInsert... rows) {
+ return builder(table, ImmutableList.copyOf(rows));
+ }
+
+ /**
+ * Returns a builder for an {@code InsertAllRequest} object given the destination table.
+ */
+ public static Builder builder(String datasetId, String tableId) {
+ return new Builder().table(TableId.of(datasetId, tableId));
+ }
+
+ /**
+ * Returns a builder for an {@code InsertAllRequest} object given the destination table and the
+ * rows to insert.
+ */
+ public static Builder builder(String datasetId, String tableId, Iterable rows) {
+ return builder(TableId.of(datasetId, tableId), rows);
+ }
+
+ /**
+ * Returns a builder for an {@code InsertAllRequest} object given the destination table and the
+ * rows to insert.
+ */
+ public static Builder builder(String datasetId, String tableId, RowToInsert... rows) {
+ return builder(TableId.of(datasetId, tableId), rows);
+ }
+
+ /**
+ * Returns a builder for an {@code InsertAllRequest} object given the destination table and the
+ * rows to insert.
+ */
+ public static Builder builder(TableInfo tableInfo, Iterable rows) {
+ return builder(tableInfo.tableId(), rows);
+ }
+
+ /**
+ * Returns a builder for an {@code InsertAllRequest} object given the destination table and the
+ * rows to insert.
+ */
+ public static Builder builder(TableInfo tableInfo, RowToInsert... rows) {
+ return builder(tableInfo.tableId(), rows);
+ }
+
+ /**
+ * Returns a {@code InsertAllRequest} object given the destination table and the rows to insert.
+ */
+ public static InsertAllRequest of(TableId tableId, Iterable rows) {
+ return builder(tableId, rows).build();
+ }
+
+ /**
+ * Returns a {@code InsertAllRequest} object given the destination table and the rows to insert.
+ */
+ public static InsertAllRequest of(TableId tableId, RowToInsert... rows) {
+ return builder(tableId, rows).build();
+ }
+
+ /**
+ * Returns a {@code InsertAllRequest} object given the destination table and the rows to insert.
+ */
+ public static InsertAllRequest of(String datasetId, String tableId, Iterable rows) {
+ return builder(datasetId, tableId, rows).build();
+ }
+
+ /**
+ * Returns a {@code InsertAllRequest} object given the destination table and the rows to insert.
+ */
+ public static InsertAllRequest of(String datasetId, String tableId, RowToInsert... rows) {
+ return builder(datasetId, tableId, rows).build();
+ }
+
+ /**
+ * Returns a {@code InsertAllRequest} object given the destination table and the rows to insert.
+ */
+ public static InsertAllRequest of(TableInfo tableInfo, Iterable rows) {
+ return builder(tableInfo.tableId(), rows).build();
+ }
+
+ /**
+ * Returns a {@code InsertAllRequest} object given the destination table and the rows to insert.
+ */
+ public static InsertAllRequest of(TableInfo tableInfo, RowToInsert... rows) {
+ return builder(tableInfo.tableId(), rows).build();
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("table", table)
+ .add("rows", rows)
+ .add("ignoreUnknownValues", ignoreUnknownValues)
+ .add("skipInvalidRows", skipInvalidRows)
+ .add("templateSuffix", templateSuffix)
+ .toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(table, rows, ignoreUnknownValues, skipInvalidRows, templateSuffix);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof InsertAllRequest)) {
+ return false;
+ }
+ InsertAllRequest other = (InsertAllRequest) obj;
+ return Objects.equals(table, other.table)
+ && Objects.equals(rows, other.rows)
+ && Objects.equals(ignoreUnknownValues, other.ignoreUnknownValues)
+ && Objects.equals(skipInvalidRows, other.skipInvalidRows)
+ && Objects.equals(templateSuffix, other.templateSuffix);
+ }
+}
diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/InsertAllResponse.java b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/InsertAllResponse.java
new file mode 100644
index 000000000000..992c5d851bbc
--- /dev/null
+++ b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/InsertAllResponse.java
@@ -0,0 +1,121 @@
+/*
+ * 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.ErrorProto;
+import com.google.api.services.bigquery.model.TableDataInsertAllResponse;
+import com.google.api.services.bigquery.model.TableDataInsertAllResponse.InsertErrors;
+import com.google.common.base.Function;
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Google Cloud BigQuery insert all response. Objects of this class possibly contain errors for an
+ * {@link InsertAllRequest}. If a row failed to be inserted, the non-empty list of errors associated
+ * to that row's index can be obtained with {@link InsertAllResponse#errorsFor(long)}.
+ * {@link InsertAllResponse#insertErrors()} can be used to return all errors caused by a
+ * {@link InsertAllRequest} as a map.
+ */
+public class InsertAllResponse implements Serializable {
+
+ private static final long serialVersionUID = -6934152676514098452L;
+
+ private final Map> insertErrors;
+
+ InsertAllResponse(Map> insertErrors) {
+ this.insertErrors = insertErrors != null ? ImmutableMap.copyOf(insertErrors)
+ : ImmutableMap.>of();
+ }
+
+ /**
+ * Returns all insertion errors as a map whose keys are indexes of rows that failed to insert.
+ * Each failed row index is associated with a non-empty list of {@link BigQueryError}.
+ */
+ public Map> insertErrors() {
+ return insertErrors;
+ }
+
+ /**
+ * Returns errors for the provided row index. If no error exists returns {@code null}.
+ */
+ public List errorsFor(long index) {
+ return insertErrors.get(index);
+ }
+
+ /**
+ * Returns {@code true} if no row insertion failed, {@code false} otherwise. If {@code false}
+ * {@link #insertErrors()} returns an empty map.
+ */
+ public boolean hasErrors() {
+ return !insertErrors.isEmpty();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(insertErrors);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof InsertAllResponse
+ && Objects.equals(insertErrors, ((InsertAllResponse) obj).insertErrors);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this).add("insertErrors", insertErrors).toString();
+ }
+
+ TableDataInsertAllResponse toPb() {
+ TableDataInsertAllResponse responsePb = new TableDataInsertAllResponse();
+ if (!insertErrors.isEmpty()) {
+ responsePb.setInsertErrors(ImmutableList.copyOf(Iterables.transform(insertErrors.entrySet(),
+ new Function>, InsertErrors>() {
+ @Override
+ public InsertErrors apply(Map.Entry> entry) {
+ return new InsertErrors()
+ .setIndex(entry.getKey())
+ .setErrors(Lists.transform(entry.getValue(), BigQueryError.TO_PB_FUNCTION));
+ }
+ })));
+ }
+ return responsePb;
+ }
+
+ static InsertAllResponse fromPb(TableDataInsertAllResponse responsePb) {
+ Map> insertErrors = null;
+ if (responsePb.getInsertErrors() != null) {
+ List errorsPb = responsePb.getInsertErrors();
+ insertErrors = Maps.newHashMapWithExpectedSize(errorsPb.size());
+ for (InsertErrors errorPb : errorsPb) {
+ insertErrors.put(errorPb.getIndex(), Lists.transform(
+ errorPb.getErrors() != null ? errorPb.getErrors() : ImmutableList.of(),
+ BigQueryError.FROM_PB_FUNCTION));
+ }
+ }
+ return new InsertAllResponse(insertErrors);
+ }
+}
diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Job.java b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Job.java
new file mode 100644
index 000000000000..1e63344a600d
--- /dev/null
+++ b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Job.java
@@ -0,0 +1,203 @@
+/*
+ * 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 java.io.IOException;
+import java.io.ObjectInputStream;
+import java.util.Objects;
+
+/**
+ * A Google BigQuery Job.
+ *
+ * Objects of this class are immutable. To get a {@code Job} object with the most recent
+ * information use {@link #reload}. {@code Job} adds a layer of service-related functionality over
+ * {@link JobInfo}.
+ *
+ */
+public final class Job extends JobInfo {
+
+ private static final long serialVersionUID = -4324100991693024704L;
+
+ private final BigQueryOptions options;
+ private transient BigQuery bigquery;
+
+ /**
+ * A builder for {@code Job} objects.
+ */
+ public static final class Builder extends JobInfo.Builder {
+
+ private final BigQuery bigquery;
+ private final JobInfo.BuilderImpl infoBuilder;
+
+ Builder(BigQuery bigquery, JobConfiguration configuration) {
+ this.bigquery = bigquery;
+ this.infoBuilder = new JobInfo.BuilderImpl();
+ this.infoBuilder.configuration(configuration);
+ }
+
+ Builder(Job job) {
+ this.bigquery = job.bigquery;
+ this.infoBuilder = new JobInfo.BuilderImpl(job);
+ }
+
+ @Override
+ Builder etag(String etag) {
+ infoBuilder.etag(etag);
+ return this;
+ }
+
+ @Override
+ Builder id(String id) {
+ infoBuilder.id(id);
+ return this;
+ }
+
+ @Override
+ public Builder jobId(JobId jobId) {
+ infoBuilder.jobId(jobId);
+ return this;
+ }
+
+ @Override
+ Builder selfLink(String selfLink) {
+ infoBuilder.selfLink(selfLink);
+ return this;
+ }
+
+ @Override
+ Builder status(JobStatus status) {
+ infoBuilder.status(status);
+ return this;
+ }
+
+ @Override
+ Builder statistics(JobStatistics statistics) {
+ infoBuilder.statistics(statistics);
+ return this;
+ }
+
+ @Override
+ Builder userEmail(String userEmail) {
+ infoBuilder.userEmail(userEmail);
+ return this;
+ }
+
+ @Override
+ public Builder configuration(JobConfiguration configuration) {
+ infoBuilder.configuration(configuration);
+ return this;
+ }
+
+ @Override
+ public Job build() {
+ return new Job(bigquery, infoBuilder);
+ }
+ }
+
+ Job(BigQuery bigquery, JobInfo.BuilderImpl infoBuilder) {
+ super(infoBuilder);
+ this.bigquery = checkNotNull(bigquery);
+ this.options = bigquery.options();
+ }
+
+ /**
+ * Checks if this job exists.
+ *
+ * @return {@code true} if this job exists, {@code false} otherwise
+ * @throws BigQueryException upon failure
+ */
+ public boolean exists() {
+ return bigquery.getJob(jobId(), BigQuery.JobOption.fields()) != null;
+ }
+
+ /**
+ * Checks if this job has completed its execution, either failing or succeeding. If the job does
+ * not exist this method returns {@code false}. To correctly wait for job's completion check that
+ * the job exists first, using {@link #exists()}:
+ * {@code
+ * if (job.exists()) {
+ * while(!job.isDone()) {
+ * Thread.sleep(1000L);
+ * }
+ * }}
+ *
+ * @return {@code true} if this job is in {@link JobStatus.State#DONE} state, {@code false} if the
+ * state is not {@link JobStatus.State#DONE} or the job does not exist
+ * @throws BigQueryException upon failure
+ */
+ public boolean isDone() {
+ Job job = bigquery.getJob(jobId(), BigQuery.JobOption.fields(BigQuery.JobField.STATUS));
+ return job != null && job.status().state() == JobStatus.State.DONE;
+ }
+
+ /**
+ * Fetches current job's latest information. Returns {@code null} if the job does not exist.
+ *
+ * @param options job options
+ * @return a {@code Job} object with latest information or {@code null} if not found
+ * @throws BigQueryException upon failure
+ */
+ public Job reload(BigQuery.JobOption... options) {
+ return bigquery.getJob(jobId().job(), options);
+ }
+
+ /**
+ * Sends a job cancel request.
+ *
+ * @return {@code true} if cancel request was sent successfully, {@code false} if job was not
+ * found
+ * @throws BigQueryException upon failure
+ */
+ public boolean cancel() {
+ return bigquery.cancel(jobId());
+ }
+
+ /**
+ * Returns the job's {@code BigQuery} object used to issue requests.
+ */
+ public BigQuery bigquery() {
+ return bigquery;
+ }
+
+ @Override
+ public Builder toBuilder() {
+ return new Builder(this);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof Job
+ && Objects.equals(toPb(), ((Job) obj).toPb())
+ && Objects.equals(options, ((Job) obj).options);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), options);
+ }
+
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ in.defaultReadObject();
+ this.bigquery = options.service();
+ }
+
+ static Job fromPb(BigQuery bigquery, com.google.api.services.bigquery.model.Job jobPb) {
+ return new Job(bigquery, new JobInfo.BuilderImpl(jobPb));
+ }
+}
diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobConfiguration.java b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobConfiguration.java
new file mode 100644
index 000000000000..2244969567ef
--- /dev/null
+++ b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobConfiguration.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2016 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.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * Base class for a BigQuery job configuration.
+ */
+public abstract class JobConfiguration implements Serializable {
+
+ private static final long serialVersionUID = -548132177415406526L;
+
+ private final Type type;
+
+ /**
+ * Type of a BigQuery Job.
+ */
+ enum Type {
+ /**
+ * A Copy Job copies an existing table to another new or existing table. Instances of
+ * {@code JobConfiguration} for this type are implemented by {@link CopyJobConfiguration}.
+ */
+ COPY,
+ /**
+ * An Extract Job exports a BigQuery table to Google Cloud Storage. Instances of
+ * {@code JobConfiguration} for this type are implemented by {@link ExtractJobConfiguration}.
+ */
+ EXTRACT,
+ /**
+ * A Load Job loads data from one of several formats into a table. Instances of
+ * {@code JobConfiguration} for this type are implemented by {@link LoadJobConfiguration}.
+ */
+ LOAD,
+ /**
+ * A Query Job runs a query against BigQuery data. Instances of
+ * {@code JobConfiguration} for this type are implemented by {@link QueryJobConfiguration}.
+ */
+ QUERY
+ }
+
+ /**
+ * Base builder for job configurations.
+ *
+ * @param the job configuration type
+ * @param the job configuration builder
+ */
+ public abstract static class Builder> {
+
+ private Type type;
+
+ Builder(Type type) {
+ this.type = checkNotNull(type);
+ }
+
+ @SuppressWarnings("unchecked")
+ B self() {
+ return (B) this;
+ }
+
+ B type(Type type) {
+ this.type = checkNotNull(type);
+ return self();
+ }
+
+ /**
+ * Creates an object.
+ */
+ public abstract T build();
+ }
+
+ JobConfiguration(Builder builder) {
+ this.type = builder.type;
+ }
+
+ /**
+ * Returns the type of the job configuration.
+ */
+ public Type type() {
+ return type;
+ }
+
+ /**
+ * Returns a builder for the object.
+ */
+ public abstract Builder toBuilder();
+
+ ToStringHelper toStringHelper() {
+ return MoreObjects.toStringHelper(this).add("type", type);
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper().toString();
+ }
+
+ final int baseHashCode() {
+ return Objects.hash(type);
+ }
+
+ final boolean baseEquals(JobConfiguration jobConfiguration) {
+ return Objects.equals(toPb(), jobConfiguration.toPb());
+ }
+
+ abstract JobConfiguration setProjectId(String projectId);
+
+ abstract com.google.api.services.bigquery.model.JobConfiguration toPb();
+
+ @SuppressWarnings("unchecked")
+ static T fromPb(
+ com.google.api.services.bigquery.model.JobConfiguration configurationPb) {
+ if (configurationPb.getCopy() != null) {
+ return (T) CopyJobConfiguration.fromPb(configurationPb);
+ } else if (configurationPb.getExtract() != null) {
+ return (T) ExtractJobConfiguration.fromPb(configurationPb);
+ } else if (configurationPb.getLoad() != null) {
+ return (T) LoadJobConfiguration.fromPb(configurationPb);
+ } else if (configurationPb.getQuery() != null) {
+ return (T) QueryJobConfiguration.fromPb(configurationPb);
+ } else {
+ // never reached
+ throw new IllegalArgumentException("Job configuration is not supported");
+ }
+ }
+}
diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobId.java b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobId.java
new file mode 100644
index 000000000000..898c894f9a21
--- /dev/null
+++ b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobId.java
@@ -0,0 +1,91 @@
+/*
+ * 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.JobReference;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * Google BigQuery Job identity.
+ */
+public class JobId implements Serializable {
+
+ private static final long serialVersionUID = 1225914835379688976L;
+
+ private final String project;
+ private final String job;
+
+ /**
+ * Returns project's user-defined id.
+ */
+ public String project() {
+ return project;
+ }
+
+ /**
+ * Returns the job's user-defined id.
+ */
+ public String job() {
+ return job;
+ }
+
+ private JobId(String project, String job) {
+ this.project = project;
+ this.job = job;
+ }
+
+ /**
+ * Creates a job identity given project's and job's user-defined id.
+ */
+ public static JobId of(String project, String job) {
+ return new JobId(checkNotNull(project), checkNotNull(job));
+ }
+
+ /**
+ * Creates a job identity given only its user-defined id.
+ */
+ public static JobId of(String job) {
+ return new JobId(null, checkNotNull(job));
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof JobId && Objects.equals(toPb(), ((JobId) obj).toPb());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(project, job);
+ }
+
+ @Override
+ public String toString() {
+ return toPb().toString();
+ }
+
+ JobReference toPb() {
+ return new JobReference().setProjectId(project).setJobId(job);
+ }
+
+ static JobId fromPb(JobReference jobRef) {
+ return new JobId(jobRef.getProjectId(), jobRef.getJobId());
+ }
+}
diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobInfo.java b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobInfo.java
new file mode 100644
index 000000000000..1adf7fabafc1
--- /dev/null
+++ b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobInfo.java
@@ -0,0 +1,386 @@
+/*
+ * 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.Job;
+import com.google.common.base.Function;
+import com.google.common.base.MoreObjects;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * Google BigQuery Job information. Jobs are objects that manage asynchronous tasks such as running
+ * queries, loading data, and exporting data. Use {@link CopyJobConfiguration} for a job that
+ * copies an existing table. Use {@link ExtractJobConfiguration} for a job that exports a table to
+ * Google Cloud Storage. Use {@link LoadJobConfiguration} for a job that loads data from Google
+ * Cloud Storage into a table. Use {@link QueryJobConfiguration} for a job that runs a query.
+ *
+ * @see Jobs
+ */
+public class JobInfo implements Serializable {
+
+ static final Function FROM_PB_FUNCTION =
+ new Function() {
+ @Override
+ public JobInfo apply(Job pb) {
+ return JobInfo.fromPb(pb);
+ }
+ };
+
+ private static final long serialVersionUID = -3272941007234620265L;
+
+ private final String etag;
+ private final String id;
+ private final JobId jobId;
+ private final String selfLink;
+ private final JobStatus status;
+ private final JobStatistics statistics;
+ private final String userEmail;
+ private final JobConfiguration configuration;
+
+ /**
+ * Specifies whether the job is allowed to create new tables.
+ */
+ public enum CreateDisposition {
+ /**
+ * Configures the job to create the table if it does not exist.
+ */
+ CREATE_IF_NEEDED,
+
+ /**
+ * Configures the job to fail with a not-found error if the table does not exist.
+ */
+ CREATE_NEVER
+ }
+
+ /**
+ * Specifies the action that occurs if the destination table already exists.
+ */
+ public enum WriteDisposition {
+ /**
+ * Configures the job to overwrite the table data if table already exists.
+ */
+ WRITE_TRUNCATE,
+
+ /**
+ * Configures the job to append data to the table if it already exists.
+ */
+ WRITE_APPEND,
+
+ /**
+ * Configures the job to fail with a duplicate error if the table already exists.
+ */
+ WRITE_EMPTY
+ }
+
+ /**
+ * A builder for {@code JobInfo} objects.
+ */
+ public abstract static class Builder {
+
+ abstract Builder etag(String etag);
+
+ abstract Builder id(String id);
+
+ /**
+ * Sets the job identity.
+ */
+ public abstract Builder jobId(JobId jobId);
+
+ abstract Builder selfLink(String selfLink);
+
+ abstract Builder status(JobStatus status);
+
+ abstract Builder statistics(JobStatistics statistics);
+
+ abstract Builder userEmail(String userEmail);
+
+ /**
+ * Sets a configuration for the {@code JobInfo} object. Use {@link CopyJobConfiguration} for a
+ * job that copies an existing table. Use {@link ExtractJobConfiguration} for a job that exports
+ * a table to Google Cloud Storage. Use {@link LoadJobConfiguration} for a job that loads data
+ * from Google Cloud Storage into a table. Use {@link QueryJobConfiguration} for a job that runs
+ * a query.
+ */
+ public abstract Builder configuration(JobConfiguration configuration);
+
+ /**
+ * Creates a {@code JobInfo} object.
+ */
+ public abstract JobInfo build();
+ }
+
+ static final class BuilderImpl extends Builder {
+
+ private String etag;
+ private String id;
+ private JobId jobId;
+ private String selfLink;
+ private JobStatus status;
+ private JobStatistics statistics;
+ private String userEmail;
+ private JobConfiguration configuration;
+
+ BuilderImpl() {}
+
+ BuilderImpl(JobInfo jobInfo) {
+ this.etag = jobInfo.etag;
+ this.id = jobInfo.id;
+ this.jobId = jobInfo.jobId;
+ this.selfLink = jobInfo.selfLink;
+ this.status = jobInfo.status;
+ this.statistics = jobInfo.statistics;
+ this.userEmail = jobInfo.userEmail;
+ this.configuration = jobInfo.configuration;
+ }
+
+ BuilderImpl(Job jobPb) {
+ this.etag = jobPb.getEtag();
+ this.id = jobPb.getId();
+ if (jobPb.getJobReference() != null) {
+ this.jobId = JobId.fromPb(jobPb.getJobReference());
+ }
+ this.selfLink = jobPb.getSelfLink();
+ if (jobPb.getStatus() != null) {
+ this.status = JobStatus.fromPb(jobPb.getStatus());
+ }
+ if (jobPb.getStatistics() != null) {
+ this.statistics = JobStatistics.fromPb(jobPb.getStatistics());
+ }
+ this.userEmail = jobPb.getUserEmail();
+ this.configuration = JobConfiguration.fromPb(jobPb.getConfiguration());
+ }
+
+ @Override
+ Builder etag(String etag) {
+ this.etag = etag;
+ return this;
+ }
+
+ @Override
+ Builder id(String id) {
+ this.id = id;
+ return this;
+ }
+
+ @Override
+ public Builder jobId(JobId jobId) {
+ this.jobId = jobId;
+ return this;
+ }
+
+ @Override
+ Builder selfLink(String selfLink) {
+ this.selfLink = selfLink;
+ return this;
+ }
+
+ @Override
+ Builder status(JobStatus status) {
+ this.status = status;
+ return this;
+ }
+
+ @Override
+ Builder statistics(JobStatistics statistics) {
+ this.statistics = statistics;
+ return this;
+ }
+
+ @Override
+ Builder userEmail(String userEmail) {
+ this.userEmail = userEmail;
+ return this;
+ }
+
+ @Override
+ public Builder configuration(JobConfiguration configuration) {
+ this.configuration = configuration;
+ return this;
+ }
+
+ @Override
+ public JobInfo build() {
+ return new JobInfo(this);
+ }
+ }
+
+ JobInfo(BuilderImpl builder) {
+ this.jobId = builder.jobId;
+ this.etag = builder.etag;
+ this.id = builder.id;
+ this.selfLink = builder.selfLink;
+ this.status = builder.status;
+ this.statistics = builder.statistics;
+ this.userEmail = builder.userEmail;
+ this.configuration = builder.configuration;
+ }
+
+ /**
+ * Returns the hash of the job resource.
+ */
+ public String etag() {
+ return etag;
+ }
+
+ /**
+ * Returns an opaque id for the job.
+ */
+ public String id() {
+ return id;
+ }
+
+ /**
+ * Returns the job identity.
+ */
+ public JobId jobId() {
+ return jobId;
+ }
+
+ /**
+ * Returns an URL that can be used to access the resource again. The returned URL can be used for
+ * GET requests.
+ */
+ public String selfLink() {
+ return selfLink;
+ }
+
+ /**
+ * Returns the status of this job. Examine this value when polling an asynchronous job to see if
+ * the job is complete.
+ */
+ public JobStatus status() {
+ return status;
+ }
+
+ /**
+ * Returns information about the job, including starting time and ending time of the job.
+ */
+ @SuppressWarnings("unchecked")
+ public S statistics() {
+ return (S) statistics;
+ }
+
+ /**
+ * Returns the email address of the user who ran the job.
+ */
+ public String userEmail() {
+ return userEmail;
+ }
+
+ /**
+ * Returns the job's configuration.
+ */
+ @SuppressWarnings("unchecked")
+ public C configuration() {
+ return (C) configuration;
+ }
+
+ /**
+ * Returns a builder for the job object.
+ */
+ public Builder toBuilder() {
+ return new BuilderImpl(this);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("job", jobId)
+ .add("status", status)
+ .add("statistics", statistics)
+ .add("userEmail", userEmail)
+ .add("etag", etag)
+ .add("id", id)
+ .add("selfLink", selfLink)
+ .add("configuration", configuration)
+ .toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(jobId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj != null
+ && obj.getClass().equals(JobInfo.class)
+ && Objects.equals(toPb(), ((JobInfo) obj).toPb());
+ }
+
+ JobInfo setProjectId(String projectId) {
+ return toBuilder().configuration(configuration.setProjectId(projectId)).build();
+ }
+
+ Job toPb() {
+ Job jobPb = new Job();
+ jobPb.setEtag(etag);
+ jobPb.setId(id);
+ jobPb.setSelfLink(selfLink);
+ jobPb.setUserEmail(userEmail);
+ if (jobId != null) {
+ jobPb.setJobReference(jobId.toPb());
+ }
+ if (status != null) {
+ jobPb.setStatus(status.toPb());
+ }
+ if (statistics != null) {
+ jobPb.setStatistics(statistics.toPb());
+ }
+ jobPb.setConfiguration(configuration.toPb());
+ return jobPb;
+ }
+
+ /**
+ * Returns a builder for a {@code JobInfo} object given the job configuration. Use
+ * {@link CopyJobConfiguration} for a job that copies an existing table. Use
+ * {@link ExtractJobConfiguration} for a job that exports a table to Google Cloud Storage. Use
+ * {@link LoadJobConfiguration} for a job that loads data from Google Cloud Storage into a table.
+ * Use {@link QueryJobConfiguration} for a job that runs a query.
+ */
+ public static Builder builder(JobConfiguration configuration) {
+ return new BuilderImpl().configuration(configuration);
+ }
+
+ /**
+ * Returns a {@code JobInfo} object given the job configuration. Use {@link CopyJobConfiguration}
+ * for a job that copies an existing table. Use {@link ExtractJobConfiguration} for a job that
+ * exports a table to Google Cloud Storage. Use {@link LoadJobConfiguration} for a job that loads
+ * data from Google Cloud Storage into a table. Use {@link QueryJobConfiguration} for a job that
+ * runs a query.
+ */
+ public static JobInfo of(JobConfiguration configuration) {
+ return builder(configuration).build();
+ }
+
+ /**
+ * Returns a builder for a {@code JobInfo} object given the job identity and configuration. Use
+ * {@link CopyJobConfiguration} for a job that copies an existing table. Use
+ * {@link ExtractJobConfiguration} for a job that exports a table to Google Cloud Storage. Use
+ * {@link LoadJobConfiguration} for a job that loads data from Google Cloud Storage into a table.
+ * Use {@link QueryJobConfiguration} for a job that runs a query.
+ */
+ public static JobInfo of(JobId jobId, JobConfiguration configuration) {
+ return builder(configuration).jobId(jobId).build();
+ }
+
+ static JobInfo fromPb(Job jobPb) {
+ return new BuilderImpl(jobPb).build();
+ }
+}
diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobStatistics.java b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobStatistics.java
new file mode 100644
index 000000000000..34e4917921ba
--- /dev/null
+++ b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobStatistics.java
@@ -0,0 +1,516 @@
+package com.google.gcloud.bigquery;
+
+import com.google.api.services.bigquery.model.JobStatistics2;
+import com.google.api.services.bigquery.model.JobStatistics3;
+import com.google.api.services.bigquery.model.JobStatistics4;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.collect.Lists;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A Google BigQuery Job statistics.
+ */
+public class JobStatistics implements Serializable {
+
+ private static final long serialVersionUID = 1433024714741660399L;
+
+ private final Long creationTime;
+ private final Long endTime;
+ private final Long startTime;
+
+ /**
+ * A Google BigQuery Extract Job statistics.
+ */
+ public static class ExtractStatistics extends JobStatistics {
+
+ private static final long serialVersionUID = -1566598819212767373L;
+
+ private final List destinationUriFileCounts;
+
+ static final class Builder extends JobStatistics.Builder {
+
+ private List destinationUriFileCounts;
+
+ private Builder() {}
+
+ private Builder(com.google.api.services.bigquery.model.JobStatistics statisticsPb) {
+ super(statisticsPb);
+ this.destinationUriFileCounts = statisticsPb.getExtract().getDestinationUriFileCounts();
+ }
+
+ Builder destinationUriFileCounts(List destinationUriFileCounts) {
+ this.destinationUriFileCounts = destinationUriFileCounts;
+ return self();
+ }
+
+ @Override
+ ExtractStatistics build() {
+ return new ExtractStatistics(this);
+ }
+ }
+
+ private ExtractStatistics(Builder builder) {
+ super(builder);
+ this.destinationUriFileCounts = builder.destinationUriFileCounts;
+ }
+
+ /**
+ * Returns the number of files per destination URI or URI pattern specified in the extract job.
+ * These values will be in the same order as the URIs specified by
+ * {@link ExtractJobConfiguration#destinationUris()}.
+ */
+ public List destinationUriFileCounts() {
+ return destinationUriFileCounts;
+ }
+
+ @Override
+ ToStringHelper toStringHelper() {
+ return super.toStringHelper().add("destinationUriFileCounts", destinationUriFileCounts);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof ExtractStatistics
+ && Objects.equals(toPb(), ((ExtractStatistics) obj).toPb());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), destinationUriFileCounts);
+ }
+
+ @Override
+ com.google.api.services.bigquery.model.JobStatistics toPb() {
+ com.google.api.services.bigquery.model.JobStatistics statisticsPb = super.toPb();
+ return statisticsPb.setExtract(
+ new JobStatistics4().setDestinationUriFileCounts(destinationUriFileCounts));
+ }
+
+ static Builder builder() {
+ return new Builder();
+ }
+
+ @SuppressWarnings("unchecked")
+ static ExtractStatistics fromPb(
+ com.google.api.services.bigquery.model.JobStatistics statisticPb) {
+ return new Builder(statisticPb).build();
+ }
+ }
+
+ /**
+ * A Google BigQuery Load Job statistics.
+ */
+ public static class LoadStatistics extends JobStatistics {
+
+ private static final long serialVersionUID = -707369246536309215L;
+
+ private final Long inputBytes;
+ private final Long inputFiles;
+ private final Long outputBytes;
+ private final Long outputRows;
+
+ static final class Builder extends JobStatistics.Builder {
+
+ private Long inputBytes;
+ private Long inputFiles;
+ private Long outputBytes;
+ private Long outputRows;
+
+ private Builder() {}
+
+ private Builder(com.google.api.services.bigquery.model.JobStatistics statisticsPb) {
+ super(statisticsPb);
+ this.inputBytes = statisticsPb.getLoad().getInputFileBytes();
+ this.inputFiles = statisticsPb.getLoad().getInputFiles();
+ this.outputBytes = statisticsPb.getLoad().getOutputBytes();
+ this.outputRows = statisticsPb.getLoad().getOutputRows();
+ }
+
+ Builder inputBytes(Long inputBytes) {
+ this.inputBytes = inputBytes;
+ return self();
+ }
+
+ Builder inputFiles(Long inputFiles) {
+ this.inputFiles = inputFiles;
+ return self();
+ }
+
+ Builder outputBytes(Long outputBytes) {
+ this.outputBytes = outputBytes;
+ return self();
+ }
+
+ Builder outputRows(Long outputRows) {
+ this.outputRows = outputRows;
+ return self();
+ }
+
+ @Override
+ LoadStatistics build() {
+ return new LoadStatistics(this);
+ }
+ }
+
+ private LoadStatistics(Builder builder) {
+ super(builder);
+ this.inputBytes = builder.inputBytes;
+ this.inputFiles = builder.inputFiles;
+ this.outputBytes = builder.outputBytes;
+ this.outputRows = builder.outputRows;
+
+ }
+
+ /**
+ * Returns the number of bytes of source data in a load job.
+ */
+ public Long inputBytes() {
+ return inputBytes;
+ }
+
+ /**
+ * Returns the number of source files in a load job.
+ */
+ public Long inputFiles() {
+ return inputFiles;
+ }
+
+ /**
+ * Returns the size of the data loaded by a load job so far, in bytes.
+ */
+ public Long outputBytes() {
+ return outputBytes;
+ }
+
+ /**
+ * Returns the number of rows loaded by a load job so far.
+ */
+ public Long outputRows() {
+ return outputRows;
+ }
+
+ @Override
+ ToStringHelper toStringHelper() {
+ return super.toStringHelper()
+ .add("inputBytes", inputBytes)
+ .add("inputFiles", inputFiles)
+ .add("outputBytes", outputBytes)
+ .add("outputRows", outputRows);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof LoadStatistics && Objects.equals(toPb(), ((LoadStatistics) obj).toPb());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), inputBytes, inputFiles, outputBytes, outputRows);
+ }
+
+ @Override
+ com.google.api.services.bigquery.model.JobStatistics toPb() {
+ JobStatistics3 loadStatisticsPb = new JobStatistics3();
+ loadStatisticsPb.setInputFileBytes(inputBytes);
+ loadStatisticsPb.setInputFiles(inputFiles);
+ loadStatisticsPb.setOutputBytes(outputBytes);
+ loadStatisticsPb.setOutputRows(outputRows);
+ return super.toPb().setLoad(loadStatisticsPb);
+ }
+
+ static Builder builder() {
+ return new Builder();
+ }
+
+ @SuppressWarnings("unchecked")
+ static LoadStatistics fromPb(com.google.api.services.bigquery.model.JobStatistics statisticPb) {
+ return new Builder(statisticPb).build();
+ }
+ }
+
+ /**
+ * A Google BigQuery Query Job statistics.
+ */
+ public static class QueryStatistics extends JobStatistics {
+
+ private static final long serialVersionUID = 7539354109226732353L;
+
+ private final Integer billingTier;
+ private final Boolean cacheHit;
+ private final Long totalBytesBilled;
+ private final Long totalBytesProcessed;
+ private final List queryPlan;
+
+ static final class Builder extends JobStatistics.Builder {
+
+ private Integer billingTier;
+ private Boolean cacheHit;
+ private Long totalBytesBilled;
+ private Long totalBytesProcessed;
+ private List queryPlan;
+
+ private Builder() {}
+
+ private Builder(com.google.api.services.bigquery.model.JobStatistics statisticsPb) {
+ super(statisticsPb);
+ this.billingTier = statisticsPb.getQuery().getBillingTier();
+ this.cacheHit = statisticsPb.getQuery().getCacheHit();
+ this.totalBytesBilled = statisticsPb.getQuery().getTotalBytesBilled();
+ this.totalBytesProcessed = statisticsPb.getQuery().getTotalBytesProcessed();
+ if (statisticsPb.getQuery().getQueryPlan() != null) {
+ this.queryPlan =
+ Lists.transform(statisticsPb.getQuery().getQueryPlan(), QueryStage.FROM_PB_FUNCTION);
+ }
+ }
+
+ Builder billingTier(Integer billingTier) {
+ this.billingTier = billingTier;
+ return self();
+ }
+
+ Builder cacheHit(Boolean cacheHit) {
+ this.cacheHit = cacheHit;
+ return self();
+ }
+
+ Builder totalBytesBilled(Long totalBytesBilled) {
+ this.totalBytesBilled = totalBytesBilled;
+ return self();
+ }
+
+ Builder totalBytesProcessed(Long totalBytesProcessed) {
+ this.totalBytesProcessed = totalBytesProcessed;
+ return self();
+ }
+
+ Builder queryPlan(List queryPlan) {
+ this.queryPlan = queryPlan;
+ return self();
+ }
+
+ @Override
+ QueryStatistics build() {
+ return new QueryStatistics(this);
+ }
+ }
+
+ private QueryStatistics(Builder builder) {
+ super(builder);
+ this.billingTier = builder.billingTier;
+ this.cacheHit = builder.cacheHit;
+ this.totalBytesBilled = builder.totalBytesBilled;
+ this.totalBytesProcessed = builder.totalBytesProcessed;
+ this.queryPlan = builder.queryPlan;
+ }
+
+ /**
+ * Returns the billing tier for the job.
+ */
+ public Integer billingTier() {
+ return billingTier;
+ }
+
+ /**
+ * Returns whether the query result was fetched from the query cache.
+ *
+ * @see
+ * Query Caching
+ */
+ public Boolean cacheHit() {
+ return cacheHit;
+ }
+
+ /**
+ * Returns the total number of bytes billed for the job.
+ */
+ public Long totalBytesBilled() {
+ return totalBytesBilled;
+ }
+
+ /**
+ * Returns the total number of bytes processed by the job.
+ */
+ public Long totalBytesProcessed() {
+ return totalBytesProcessed;
+ }
+
+ /**
+ * Returns the query plan as a list of stages or {@code null} if a query plan is not available.
+ * Each stage involves a number of steps that read from data sources, perform a series of
+ * transformations on the input, and emit an output to a future stage (or the final result). The
+ * query plan is available for a completed query job and is retained for 7 days.
+ *
+ * @see Query Plan
+ */
+ public List queryPlan() {
+ return queryPlan;
+ }
+
+ @Override
+ ToStringHelper toStringHelper() {
+ return super.toStringHelper()
+ .add("billingTier", billingTier)
+ .add("cacheHit", cacheHit)
+ .add("totalBytesBilled", totalBytesBilled)
+ .add("totalBytesProcessed", totalBytesProcessed)
+ .add("queryPlan", queryPlan);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof QueryStatistics
+ && Objects.equals(toPb(), ((QueryStatistics) obj).toPb());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), billingTier, cacheHit, totalBytesBilled,
+ totalBytesProcessed, queryPlan);
+ }
+
+ @Override
+ com.google.api.services.bigquery.model.JobStatistics toPb() {
+ JobStatistics2 queryStatisticsPb = new JobStatistics2();
+ queryStatisticsPb.setBillingTier(billingTier);
+ queryStatisticsPb.setCacheHit(cacheHit);
+ queryStatisticsPb.setTotalBytesBilled(totalBytesBilled);
+ queryStatisticsPb.setTotalBytesProcessed(totalBytesProcessed);
+ if (queryPlan != null) {
+ queryStatisticsPb.setQueryPlan(Lists.transform(queryPlan, QueryStage.TO_PB_FUNCTION));
+ }
+ return super.toPb().setQuery(queryStatisticsPb);
+ }
+
+ static Builder builder() {
+ return new Builder();
+ }
+
+ @SuppressWarnings("unchecked")
+ static QueryStatistics fromPb(
+ com.google.api.services.bigquery.model.JobStatistics statisticPb) {
+ return new Builder(statisticPb).build();
+ }
+ }
+
+ static class Builder> {
+
+ private Long creationTime;
+ private Long endTime;
+ private Long startTime;
+
+ protected Builder() {}
+
+ protected Builder(com.google.api.services.bigquery.model.JobStatistics statisticsPb) {
+ this.creationTime = statisticsPb.getCreationTime();
+ this.endTime = statisticsPb.getEndTime();
+ this.startTime = statisticsPb.getStartTime();
+ }
+
+ @SuppressWarnings("unchecked")
+ protected B self() {
+ return (B) this;
+ }
+
+ B creationTime(Long creationTime) {
+ this.creationTime = creationTime;
+ return self();
+ }
+
+ B endTime(Long endTime) {
+ this.endTime = endTime;
+ return self();
+ }
+
+ B startTime(Long startTime) {
+ this.startTime = startTime;
+ return self();
+ }
+
+ @SuppressWarnings("unchecked")
+ T build() {
+ return (T) new JobStatistics(this);
+ }
+ }
+
+ protected JobStatistics(Builder builder) {
+ this.creationTime = builder.creationTime;
+ this.endTime = builder.endTime;
+ this.startTime = builder.startTime;
+ }
+
+ /**
+ * Returns the creation time of the job in milliseconds since epoch.
+ */
+ public Long creationTime() {
+ return creationTime;
+ }
+
+ /**
+ * Returns the end time of the job in milliseconds since epoch. Returns {@code null} if the
+ * job has not finished yet.
+ */
+ public Long endTime() {
+ return endTime;
+ }
+
+ /**
+ * Returns the start time of the job in milliseconds since epoch. Returns {@code null} if the
+ * job has not started yet.
+ */
+ public Long startTime() {
+ return startTime;
+ }
+
+ ToStringHelper toStringHelper() {
+ return MoreObjects.toStringHelper(this)
+ .add("creationTime", creationTime)
+ .add("endTime", endTime)
+ .add("startTime", startTime);
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper().toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(creationTime, endTime, startTime);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof JobStatistics && Objects.equals(toPb(), ((JobStatistics) obj).toPb());
+ }
+
+ com.google.api.services.bigquery.model.JobStatistics toPb() {
+ com.google.api.services.bigquery.model.JobStatistics statistics =
+ new com.google.api.services.bigquery.model.JobStatistics();
+ statistics.setCreationTime(creationTime);
+ statistics.setEndTime(endTime);
+ statistics.setStartTime(startTime);
+ return statistics;
+ }
+
+ static Builder builder() {
+ return new Builder();
+ }
+
+ @SuppressWarnings("unchecked")
+ static T fromPb(
+ com.google.api.services.bigquery.model.JobStatistics statisticPb) {
+ if (statisticPb.getLoad() != null) {
+ return (T) LoadStatistics.fromPb(statisticPb);
+ } else if (statisticPb.getExtract() != null) {
+ return (T) ExtractStatistics.fromPb(statisticPb);
+ } else if (statisticPb.getQuery() != null) {
+ return (T) QueryStatistics.fromPb(statisticPb);
+ } else {
+ return (T) new Builder(statisticPb).build();
+ }
+ }
+}
diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobStatus.java b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobStatus.java
new file mode 100644
index 000000000000..738a644a5dde
--- /dev/null
+++ b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobStatus.java
@@ -0,0 +1,130 @@
+package com.google.gcloud.bigquery;
+
+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;
+
+/**
+ * A Google BigQuery Job status. Objects of this class can be examined when polling an asynchronous
+ * job to see if the job completed.
+ */
+public class JobStatus implements Serializable {
+
+ private static final long serialVersionUID = -714976456815445365L;
+
+ /**
+ * Possible states that a BigQuery Job can assume.
+ */
+ public enum State {
+ /**
+ * The BigQuery Job is waiting to be executed.
+ */
+ PENDING,
+
+ /**
+ * The BigQuery Job is being executed.
+ */
+ RUNNING,
+
+ /**
+ * The BigQuery Job has completed either succeeding or failing. If failed {@link #error()} will
+ * be non-null.
+ */
+ DONE
+ }
+
+ private final State state;
+ private final BigQueryError error;
+ private final List executionErrors;
+
+ JobStatus(State state) {
+ this.state = state;
+ this.error = null;
+ this.executionErrors = null;
+ }
+
+ JobStatus(State state, BigQueryError error, List executionErrors) {
+ this.state = state;
+ this.error = error;
+ this.executionErrors = executionErrors != null ? ImmutableList.copyOf(executionErrors) : null;
+ }
+
+ /**
+ * Returns the state of the job. A {@link State#PENDING} job is waiting to be executed. A
+ * {@link State#RUNNING} is being executed. A {@link State#DONE} job has completed either
+ * succeeding or failing. If failed {@link #error()} will be non-null.
+ */
+ public State state() {
+ return state;
+ }
+
+ /**
+ * Returns the final error result of the job. If present, indicates that the job has completed
+ * and was unsuccessful.
+ *
+ * @see
+ * Troubleshooting Errors
+ */
+ public BigQueryError error() {
+ return error;
+ }
+
+ /**
+ * Returns all errors encountered during the running of the job. Errors here do not necessarily
+ * mean that the job has completed or was unsuccessful.
+ *
+ * @see
+ * Troubleshooting Errors
+ */
+ public List executionErrors() {
+ return executionErrors;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("state", state)
+ .add("error", error)
+ .add("executionErrors", executionErrors)
+ .toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(state, error, executionErrors);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof JobStatus && Objects.equals(toPb(), ((JobStatus) obj).toPb());
+ }
+
+ com.google.api.services.bigquery.model.JobStatus toPb() {
+ com.google.api.services.bigquery.model.JobStatus statusPb =
+ new com.google.api.services.bigquery.model.JobStatus();
+ if (state != null) {
+ statusPb.setState(state.toString());
+ }
+ if (error != null) {
+ statusPb.setErrorResult(error.toPb());
+ }
+ if (executionErrors != null) {
+ statusPb.setErrors(Lists.transform(executionErrors, BigQueryError.TO_PB_FUNCTION));
+ }
+ return statusPb;
+ }
+
+ static JobStatus fromPb(com.google.api.services.bigquery.model.JobStatus statusPb) {
+ List allErrors = null;
+ if (statusPb.getErrors() != null) {
+ allErrors = Lists.transform(statusPb.getErrors(), BigQueryError.FROM_PB_FUNCTION);
+ }
+ BigQueryError error =
+ statusPb.getErrorResult() != null ? BigQueryError.fromPb(statusPb.getErrorResult()) : null;
+ return new JobStatus(State.valueOf(statusPb.getState()), error, allErrors);
+ }
+}
diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/LoadConfiguration.java b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/LoadConfiguration.java
new file mode 100644
index 000000000000..223a25a478e0
--- /dev/null
+++ b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/LoadConfiguration.java
@@ -0,0 +1,165 @@
+/*
+ * 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.gcloud.bigquery.JobInfo.CreateDisposition;
+import com.google.gcloud.bigquery.JobInfo.WriteDisposition;
+
+import java.util.List;
+
+/**
+ * Common interface for a load configuration. A load configuration
+ * ({@link WriteChannelConfiguration}) can be used to load data into a table with a
+ * {@link com.google.gcloud.WriteChannel} ({@link BigQuery#writer(WriteChannelConfiguration)}).
+ * A load configuration ({@link LoadJobConfiguration}) can also be used to create a load job
+ * ({@link JobInfo#of(JobConfiguration)}).
+ */
+public interface LoadConfiguration {
+
+ interface Builder {
+
+ /**
+ * Sets the destination table to load the data into.
+ */
+ Builder destinationTable(TableId destinationTable);
+
+ /**
+ * Sets whether the job is allowed to create new tables.
+ *
+ * @see
+ * Create Disposition
+ */
+ Builder createDisposition(CreateDisposition createDisposition);
+
+ /**
+ * Sets the action that should occur if the destination table already exists.
+ *
+ * @see
+ * Write Disposition
+ */
+ Builder writeDisposition(WriteDisposition writeDisposition);
+
+ /**
+ * Sets the source format, and possibly some parsing options, of the external data. Supported
+ * formats are {@code CSV}, {@code NEWLINE_DELIMITED_JSON} and {@code DATASTORE_BACKUP}. If not
+ * specified, {@code CSV} format is assumed.
+ *
+ *
+ * Source Format
+ */
+ Builder formatOptions(FormatOptions formatOptions);
+
+ /**
+ * Sets the maximum number of bad records that BigQuery can ignore when running the job. If the
+ * number of bad records exceeds this value, an invalid error is returned in the job result.
+ * By default no bad record is ignored.
+ */
+ Builder maxBadRecords(Integer maxBadRecords);
+
+ /**
+ * Sets the schema for the destination table. The schema can be omitted if the destination table
+ * already exists, or if you're loading data from a Google Cloud Datastore backup (i.e.
+ * {@code DATASTORE_BACKUP} format option).
+ */
+ Builder schema(Schema schema);
+
+ /**
+ * Sets whether BigQuery should allow extra values that are not represented in the table schema.
+ * If {@code true}, the extra values are ignored. If {@code 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. By default unknown values are not allowed.
+ */
+ Builder ignoreUnknownValues(Boolean ignoreUnknownValues);
+
+ /**
+ * Sets which entity properties to load into BigQuery from a Cloud Datastore backup. This field
+ * is only used if the source format is set to {@code DATASTORE_BACKUP}. Property names are case
+ * sensitive and must be top-level properties. If no properties are specified, BigQuery loads
+ * all properties. If any named property isn't found in the Cloud Datastore backup, an invalid
+ * error is returned in the job result.
+ */
+ Builder projectionFields(List projectionFields);
+
+ LoadConfiguration build();
+ }
+
+ /**
+ * Returns the destination table to load the data into.
+ */
+ TableId destinationTable();
+
+ /**
+ * Returns whether the job is allowed to create new tables.
+ *
+ * @see
+ * Create Disposition
+ */
+ CreateDisposition createDisposition();
+
+ /**
+ * Returns the action that should occur if the destination table already exists.
+ *
+ * @see
+ * Write Disposition
+ */
+ WriteDisposition writeDisposition();
+
+ /**
+ * Returns additional properties used to parse CSV data (used when {@link #format()} is set
+ * to CSV). Returns {@code null} if not set.
+ */
+ CsvOptions csvOptions();
+
+ /**
+ * Returns the maximum number of bad records that BigQuery can ignore when running the job. If the
+ * number of bad records exceeds this value, an invalid error is returned in the job result.
+ * By default no bad record is ignored.
+ */
+ Integer maxBadRecords();
+
+ /**
+ * Returns the schema for the destination table, if set. Returns {@code null} otherwise.
+ */
+ Schema schema();
+
+ /**
+ * Returns the format of the data files.
+ */
+ String format();
+
+ /**
+ * Returns whether BigQuery should allow extra values that are not represented in the table
+ * schema. If {@code true}, the extra values are ignored. If {@code true}, 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. By default unknown values are not allowed.
+ */
+ Boolean ignoreUnknownValues();
+
+ /**
+ * Returns which entity properties to load into BigQuery from a Cloud Datastore backup. This field
+ * is only used if the source format is set to {@code DATASTORE_BACKUP}. Property names are case
+ * sensitive and must be top-level properties. If no properties are specified, BigQuery loads
+ * all properties. If any named property isn't found in the Cloud Datastore backup, an invalid
+ * error is returned in the job result.
+ */
+ List projectionFields();
+
+ /**
+ * Returns a builder for the load configuration object.
+ */
+ Builder toBuilder();
+}
diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/LoadJobConfiguration.java b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/LoadJobConfiguration.java
new file mode 100644
index 000000000000..9c9fa7a769b6
--- /dev/null
+++ b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/LoadJobConfiguration.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright 2016 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.JobConfigurationLoad;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Google BigQuery load job configuration. A load job loads data from one of several formats into a
+ * table. Data is provided as URIs that point to objects in Google Cloud Storage. Load job
+ * configurations have {@link JobConfiguration.Type#LOAD} type.
+ */
+public final class LoadJobConfiguration extends JobConfiguration implements LoadConfiguration {
+
+ private static final long serialVersionUID = -2673554846792429829L;
+
+ private final List sourceUris;
+ private final TableId destinationTable;
+ private final JobInfo.CreateDisposition createDisposition;
+ private final JobInfo.WriteDisposition writeDisposition;
+ private final FormatOptions formatOptions;
+ private final Integer maxBadRecords;
+ private final Schema schema;
+ private final Boolean ignoreUnknownValues;
+ private final List projectionFields;
+
+ public static final class Builder
+ extends JobConfiguration.Builder
+ implements LoadConfiguration.Builder {
+
+ private List sourceUris;
+ private TableId destinationTable;
+ private JobInfo.CreateDisposition createDisposition;
+ private JobInfo.WriteDisposition writeDisposition;
+ private FormatOptions formatOptions;
+ private Integer maxBadRecords;
+ private Schema schema;
+ private Boolean ignoreUnknownValues;
+ private List projectionFields;
+
+ private Builder() {
+ super(Type.LOAD);
+ }
+
+ private Builder(LoadJobConfiguration loadConfiguration) {
+ this();
+ this.destinationTable = loadConfiguration.destinationTable;
+ this.createDisposition = loadConfiguration.createDisposition;
+ this.writeDisposition = loadConfiguration.writeDisposition;
+ this.formatOptions = loadConfiguration.formatOptions;
+ this.maxBadRecords = loadConfiguration.maxBadRecords;
+ this.schema = loadConfiguration.schema;
+ this.ignoreUnknownValues = loadConfiguration.ignoreUnknownValues;
+ this.projectionFields = loadConfiguration.projectionFields;
+ this.sourceUris = loadConfiguration.sourceUris;
+ }
+
+ private Builder(com.google.api.services.bigquery.model.JobConfiguration configurationPb) {
+ this();
+ JobConfigurationLoad loadConfigurationPb = configurationPb.getLoad();
+ this.destinationTable = TableId.fromPb(loadConfigurationPb.getDestinationTable());
+ if (loadConfigurationPb.getCreateDisposition() != null) {
+ this.createDisposition =
+ JobInfo.CreateDisposition.valueOf(loadConfigurationPb.getCreateDisposition());
+ }
+ if (loadConfigurationPb.getWriteDisposition() != null) {
+ this.writeDisposition =
+ JobInfo.WriteDisposition.valueOf(loadConfigurationPb.getWriteDisposition());
+ }
+ if (loadConfigurationPb.getSourceFormat() != null) {
+ this.formatOptions = FormatOptions.of(loadConfigurationPb.getSourceFormat());
+ }
+ if (loadConfigurationPb.getAllowJaggedRows() != null
+ || loadConfigurationPb.getAllowQuotedNewlines() != null
+ || loadConfigurationPb.getEncoding() != null
+ || loadConfigurationPb.getFieldDelimiter() != null
+ || loadConfigurationPb.getQuote() != null
+ || loadConfigurationPb.getSkipLeadingRows() != null) {
+ CsvOptions.Builder builder = CsvOptions.builder()
+ .allowJaggedRows(loadConfigurationPb.getAllowJaggedRows())
+ .allowQuotedNewLines(loadConfigurationPb.getAllowQuotedNewlines())
+ .encoding(loadConfigurationPb.getEncoding())
+ .fieldDelimiter(loadConfigurationPb.getFieldDelimiter())
+ .quote(loadConfigurationPb.getQuote())
+ .skipLeadingRows(loadConfigurationPb.getSkipLeadingRows());
+ this.formatOptions = builder.build();
+ }
+ this.maxBadRecords = loadConfigurationPb.getMaxBadRecords();
+ if (loadConfigurationPb.getSchema() != null) {
+ this.schema = Schema.fromPb(loadConfigurationPb.getSchema());
+ }
+ this.ignoreUnknownValues = loadConfigurationPb.getIgnoreUnknownValues();
+ this.projectionFields = loadConfigurationPb.getProjectionFields();
+ if (loadConfigurationPb.getSourceUris() != null) {
+ this.sourceUris = ImmutableList.copyOf(configurationPb.getLoad().getSourceUris());
+ }
+ }
+
+ @Override
+ public Builder destinationTable(TableId destinationTable) {
+ this.destinationTable = destinationTable;
+ return this;
+ }
+
+ @Override
+ public Builder createDisposition(JobInfo.CreateDisposition createDisposition) {
+ this.createDisposition = createDisposition;
+ return this;
+ }
+
+ @Override
+ public Builder writeDisposition(JobInfo.WriteDisposition writeDisposition) {
+ this.writeDisposition = writeDisposition;
+ return this;
+ }
+
+ @Override
+ public Builder formatOptions(FormatOptions formatOptions) {
+ this.formatOptions = formatOptions;
+ return this;
+ }
+
+ @Override
+ public Builder maxBadRecords(Integer maxBadRecords) {
+ this.maxBadRecords = maxBadRecords;
+ return this;
+ }
+
+ @Override
+ public Builder schema(Schema schema) {
+ this.schema = schema;
+ return this;
+ }
+
+ @Override
+ public Builder ignoreUnknownValues(Boolean ignoreUnknownValues) {
+ this.ignoreUnknownValues = ignoreUnknownValues;
+ return this;
+ }
+
+ @Override
+ public Builder projectionFields(List projectionFields) {
+ this.projectionFields =
+ projectionFields != null ? ImmutableList.copyOf(projectionFields) : null;
+ return this;
+ }
+
+ /**
+ * Sets the fully-qualified URIs that point to source data in Google Cloud Storage (e.g.
+ * gs://bucket/path). Each URI can contain one '*' wildcard character and it must come after the
+ * 'bucket' name.
+ */
+ public Builder sourceUris(List sourceUris) {
+ this.sourceUris = ImmutableList.copyOf(checkNotNull(sourceUris));
+ return this;
+ }
+
+ @Override
+ public LoadJobConfiguration build() {
+ return new LoadJobConfiguration(this);
+ }
+ }
+
+ private LoadJobConfiguration(Builder builder) {
+ super(builder);
+ this.sourceUris = builder.sourceUris;
+ this.destinationTable = builder.destinationTable;
+ this.createDisposition = builder.createDisposition;
+ this.writeDisposition = builder.writeDisposition;
+ this.formatOptions = builder.formatOptions;
+ this.maxBadRecords = builder.maxBadRecords;
+ this.schema = builder.schema;
+ this.ignoreUnknownValues = builder.ignoreUnknownValues;
+ this.projectionFields = builder.projectionFields;
+ }
+
+ @Override
+ public TableId destinationTable() {
+ return destinationTable;
+ }
+
+ @Override
+ public JobInfo.CreateDisposition createDisposition() {
+ return this.createDisposition;
+ }
+
+ @Override
+ public JobInfo.WriteDisposition writeDisposition() {
+ return writeDisposition;
+ }
+
+ @Override
+ public CsvOptions csvOptions() {
+ return formatOptions instanceof CsvOptions ? (CsvOptions) formatOptions : null;
+ }
+
+ @Override
+ public Integer maxBadRecords() {
+ return maxBadRecords;
+ }
+
+ @Override
+ public Schema schema() {
+ return schema;
+ }
+
+ @Override
+ public String format() {
+ return formatOptions != null ? formatOptions.type() : null;
+ }
+
+ @Override
+ public Boolean ignoreUnknownValues() {
+ return ignoreUnknownValues;
+ }
+
+ @Override
+ public List projectionFields() {
+ return projectionFields;
+ }
+
+ /**
+ * Returns the fully-qualified URIs that point to source data in Google Cloud Storage (e.g.
+ * gs://bucket/path). Each URI can contain one '*' wildcard character and it must come after the
+ * 'bucket' name.
+ */
+ public List sourceUris() {
+ return sourceUris;
+ }
+
+ @Override
+ public Builder toBuilder() {
+ return new Builder(this);
+ }
+
+ @Override
+ ToStringHelper toStringHelper() {
+ return super.toStringHelper()
+ .add("destinationTable", destinationTable)
+ .add("createDisposition", createDisposition)
+ .add("writeDisposition", writeDisposition)
+ .add("formatOptions", formatOptions)
+ .add("maxBadRecords", maxBadRecords)
+ .add("schema", schema)
+ .add("ignoreUnknownValue", ignoreUnknownValues)
+ .add("projectionFields", projectionFields)
+ .add("sourceUris", sourceUris);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof LoadJobConfiguration && baseEquals((LoadJobConfiguration) obj);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(baseHashCode(), sourceUris);
+ }
+
+ @Override
+ LoadJobConfiguration setProjectId(String projectId) {
+ return toBuilder().destinationTable(destinationTable().setProjectId(projectId)).build();
+ }
+
+ @Override
+ com.google.api.services.bigquery.model.JobConfiguration toPb() {
+ JobConfigurationLoad loadConfigurationPb = new JobConfigurationLoad();
+ loadConfigurationPb.setDestinationTable(destinationTable.toPb());
+ if (createDisposition != null) {
+ loadConfigurationPb.setCreateDisposition(createDisposition.toString());
+ }
+ if (writeDisposition != null) {
+ loadConfigurationPb.setWriteDisposition(writeDisposition.toString());
+ }
+ if (csvOptions() != null) {
+ CsvOptions csvOptions = csvOptions();
+ loadConfigurationPb.setFieldDelimiter(csvOptions.fieldDelimiter())
+ .setAllowJaggedRows(csvOptions.allowJaggedRows())
+ .setAllowQuotedNewlines(csvOptions.allowQuotedNewLines())
+ .setEncoding(csvOptions.encoding())
+ .setQuote(csvOptions.quote())
+ .setSkipLeadingRows(csvOptions.skipLeadingRows());
+ }
+ if (schema != null) {
+ loadConfigurationPb.setSchema(schema.toPb());
+ }
+ if (formatOptions != null) {
+ loadConfigurationPb.setSourceFormat(formatOptions.type());
+ }
+ loadConfigurationPb.setMaxBadRecords(maxBadRecords);
+ loadConfigurationPb.setIgnoreUnknownValues(ignoreUnknownValues);
+ loadConfigurationPb.setProjectionFields(projectionFields);
+ if (sourceUris != null) {
+ loadConfigurationPb.setSourceUris(ImmutableList.copyOf(sourceUris));
+ }
+ return new com.google.api.services.bigquery.model.JobConfiguration()
+ .setLoad(loadConfigurationPb);
+ }
+
+ /**
+ * Creates a builder for a BigQuery Load Job configuration given the destination table and source
+ * URIs.
+ */
+ public static Builder builder(TableId destinationTable, List sourceUris) {
+ return new Builder().destinationTable(destinationTable).sourceUris(sourceUris);
+ }
+
+ /**
+ * Creates a builder for a BigQuery Load Job configuration given the destination table and source
+ * URI.
+ */
+ public static Builder builder(TableId destinationTable, String sourceUri) {
+ return builder(destinationTable, ImmutableList.of(sourceUri));
+ }
+
+ /**
+ * Creates a builder for a BigQuery Load Job configuration given the destination table, format and
+ * source URIs.
+ */
+ public static Builder builder(TableId destinationTable, List sourceUris,
+ FormatOptions format) {
+ return builder(destinationTable, sourceUris).formatOptions(format);
+ }
+
+ /**
+ * Creates a builder for a BigQuery Load Job configuration given the destination table, format and
+ * source URI.
+ */
+ public static Builder builder(TableId destinationTable, String sourceUri, FormatOptions format) {
+ return builder(destinationTable, ImmutableList.of(sourceUri), format);
+ }
+
+ /**
+ * Returns a BigQuery Load Job Configuration for the given destination table and source URIs.
+ */
+ public static LoadJobConfiguration of(TableId destinationTable, List