diff --git a/bigquery/cloud-client/pom.xml b/bigquery/cloud-client/pom.xml
index f6bb3cace9d..4a7badc1757 100644
--- a/bigquery/cloud-client/pom.xml
+++ b/bigquery/cloud-client/pom.xml
@@ -39,6 +39,11 @@
google-cloud-bigquery
0.8.0-beta
+
+ joda-time
+ joda-time
+ 2.9.7
+
diff --git a/bigquery/cloud-client/src/main/java/com/example/bigquery/QueryParametersSample.java b/bigquery/cloud-client/src/main/java/com/example/bigquery/QueryParametersSample.java
new file mode 100644
index 00000000000..b2ee9362b75
--- /dev/null
+++ b/bigquery/cloud-client/src/main/java/com/example/bigquery/QueryParametersSample.java
@@ -0,0 +1,286 @@
+/*
+ Copyright 2016 Google Inc.
+
+ 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.example.bigquery;
+
+import com.google.cloud.bigquery.BigQuery;
+import com.google.cloud.bigquery.BigQueryOptions;
+import com.google.cloud.bigquery.FieldValue;
+import com.google.cloud.bigquery.QueryParameterValue;
+import com.google.cloud.bigquery.QueryRequest;
+import com.google.cloud.bigquery.QueryResponse;
+import com.google.cloud.bigquery.QueryResult;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.format.DateTimeFormatter;
+import org.joda.time.format.ISODateTimeFormat;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * A sample that demonstrates use of query parameters.
+ */
+public class QueryParametersSample {
+ private static final int ERROR_CODE = 1;
+
+ private static void printUsage() {
+ System.err.println("Usage:");
+ System.err.printf(
+ "\tmvn exec:java -Dexec.mainClass=%s -Dexec.args='%s'\n",
+ QueryParametersSample.class.getCanonicalName(),
+ "${sample}");
+ System.err.println();
+ System.err.println("${sample} can be one of: named, array, timestamp");
+ System.err.println();
+ System.err.println("Usage for ${sample}=named:");
+ System.err.printf(
+ "\tmvn exec:java -Dexec.mainClass=%s -Dexec.args='%s'\n",
+ QueryParametersSample.class.getCanonicalName(),
+ "named ${corpus} ${minWordCount}");
+ System.err.println();
+ System.err.println("Usage for sample=array:");
+ System.err.printf(
+ "\tmvn exec:java -Dexec.mainClass=%s -Dexec.args='%s'\n",
+ QueryParametersSample.class.getCanonicalName(),
+ "array ${gender} ${states...}");
+ System.err.println();
+ System.err.println("\twhere ${gender} can be on of: M, F");
+ System.err.println(
+ "\tand ${states} is any upper-case 2-letter code for U.S. a state, e.g. CA.");
+ System.err.println();
+ System.err.printf(
+ "\t\tmvn exec:java -Dexec.mainClass=%s -Dexec.args='%s'\n",
+ QueryParametersSample.class.getCanonicalName(),
+ "array F MD WA");
+ }
+
+ /**
+ * Prompts the user for the required parameters to perform a query.
+ */
+ public static void main(final String[] args) throws IOException, InterruptedException {
+ if (args.length < 1) {
+ System.err.println("Expected first argument 'sample'");
+ printUsage();
+ System.exit(ERROR_CODE);
+ }
+ String sample = args[0];
+
+ switch (sample) {
+ case "named":
+ if (args.length != 3) {
+ System.err.println("Unexpected number of arguments for named query sample.");
+ printUsage();
+ System.exit(ERROR_CODE);
+ }
+ runNamed(args[1], Long.parseLong(args[2]));
+ break;
+ case "array":
+ if (args.length < 2) {
+ System.err.println("Unexpected number of arguments for array query sample.");
+ printUsage();
+ System.exit(ERROR_CODE);
+ }
+ String gender = args[1];
+ String[] states = Arrays.copyOfRange(args, 2, args.length);
+ runArray(gender, states);
+ break;
+ case "timestamp":
+ if (args.length != 1) {
+ System.err.println("Unexpected number of arguments for timestamp query sample.");
+ printUsage();
+ System.exit(ERROR_CODE);
+ }
+ runTimestamp();
+ break;
+ default:
+ System.err.println("Got bad value for sample");
+ printUsage();
+ System.exit(ERROR_CODE);
+ }
+ }
+
+ /**
+ * Query the Shakespeare dataset for words with count at least {@code minWordCount} in the corpus
+ * {@code corpus}.
+ */
+ // [START bigquery_query_params]
+ private static void runNamed(final String corpus, final long minWordCount)
+ throws InterruptedException {
+ BigQuery bigquery =
+ new BigQueryOptions.DefaultBigqueryFactory().create(BigQueryOptions.getDefaultInstance());
+
+ String queryString = "SELECT word, word_count\n"
+ + "FROM `bigquery-public-data.samples.shakespeare`\n"
+ + "WHERE corpus = @corpus\n"
+ + "AND word_count >= @min_word_count\n"
+ + "ORDER BY word_count DESC";
+ QueryRequest queryRequest =
+ QueryRequest.newBuilder(queryString)
+ .addNamedParameter("corpus", QueryParameterValue.string(corpus))
+ .addNamedParameter("min_word_count", QueryParameterValue.int64(minWordCount))
+ // Standard SQL syntax is required for parameterized queries.
+ // See: https://cloud.google.com/bigquery/sql-reference/
+ .setUseLegacySql(false)
+ .build();
+
+ // Execute the query.
+ QueryResponse response = bigquery.query(queryRequest);
+
+ // Wait for the job to finish (if the query takes more than 10 seconds to complete).
+ while (!response.jobCompleted()) {
+ Thread.sleep(1000);
+ response = bigquery.getQueryResults(response.getJobId());
+ }
+
+ if (response.hasErrors()) {
+ throw new RuntimeException(
+ response
+ .getExecutionErrors()
+ .stream()
+ .map(err -> err.getMessage())
+ .collect(Collectors.joining("\n")));
+ }
+
+ QueryResult result = response.getResult();
+ Iterator> iter = result.iterateAll();
+
+ while (iter.hasNext()) {
+ List row = iter.next();
+ System.out.printf(
+ "%s: %d\n",
+ row.get(0).getStringValue(),
+ row.get(1).getLongValue());
+ }
+ }
+ // [END bigquery_query_params]
+
+ /**
+ * Query the baby names database to find the most popular names for a gender in a list of states.
+ */
+ // [START bigquery_query_params_arrays]
+ private static void runArray(String gender, String[] states)
+ throws InterruptedException {
+ BigQuery bigquery =
+ new BigQueryOptions.DefaultBigqueryFactory().create(BigQueryOptions.getDefaultInstance());
+
+ String queryString = "SELECT name, sum(number) as count\n"
+ + "FROM `bigquery-public-data.usa_names.usa_1910_2013`\n"
+ + "WHERE gender = @gender\n"
+ + "AND state IN UNNEST(@states)\n"
+ + "GROUP BY name\n"
+ + "ORDER BY count DESC\n"
+ + "LIMIT 10;";
+ QueryRequest queryRequest =
+ QueryRequest.newBuilder(queryString)
+ .addNamedParameter("gender", QueryParameterValue.string(gender))
+ .addNamedParameter(
+ "states",
+ QueryParameterValue.array(states, String.class))
+ // Standard SQL syntax is required for parameterized queries.
+ // See: https://cloud.google.com/bigquery/sql-reference/
+ .setUseLegacySql(false)
+ .build();
+
+ // Execute the query.
+ QueryResponse response = bigquery.query(queryRequest);
+
+ // Wait for the job to finish (if the query takes more than 10 seconds to complete).
+ while (!response.jobCompleted()) {
+ Thread.sleep(1000);
+ response = bigquery.getQueryResults(response.getJobId());
+ }
+
+ if (response.hasErrors()) {
+ throw new RuntimeException(
+ response
+ .getExecutionErrors()
+ .stream()
+ .map(err -> err.getMessage())
+ .collect(Collectors.joining("\n")));
+ }
+
+ QueryResult result = response.getResult();
+ Iterator> iter = result.iterateAll();
+
+ while (iter.hasNext()) {
+ List row = iter.next();
+ System.out.printf("%s: %d\n", row.get(0).getStringValue(), row.get(1).getLongValue());
+ }
+ }
+ // [END bigquery_query_params_arrays]
+
+ // [START bigquery_query_params_timestamps]
+ private static void runTimestamp() throws InterruptedException {
+ BigQuery bigquery =
+ new BigQueryOptions.DefaultBigqueryFactory().create(BigQueryOptions.getDefaultInstance());
+
+ // Timestamps are expected to be in the format YYYY-MM-DD HH:MM:SS.DDDDDD time_zone
+ // See: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#timestamp-type
+ DateTime timestamp = new DateTime(2016, 12, 7, 8, 0, 0, DateTimeZone.UTC);
+
+ String queryString = "SELECT TIMESTAMP_ADD(@ts_value, INTERVAL 1 HOUR);";
+ QueryRequest queryRequest =
+ QueryRequest.newBuilder(queryString)
+ .addNamedParameter(
+ "ts_value",
+ QueryParameterValue.timestamp(
+ // Timestamp takes microseconds since 1970-01-01T00:00:00 UTC
+ timestamp.getMillis() * 1000))
+ // Standard SQL syntax is required for parameterized queries.
+ // See: https://cloud.google.com/bigquery/sql-reference/
+ .setUseLegacySql(false)
+ .build();
+
+ // Execute the query.
+ QueryResponse response = bigquery.query(queryRequest);
+
+ // Wait for the job to finish (if the query takes more than 10 seconds to complete).
+ while (!response.jobCompleted()) {
+ Thread.sleep(1000);
+ response = bigquery.getQueryResults(response.getJobId());
+ }
+
+ if (response.hasErrors()) {
+ throw new RuntimeException(
+ response
+ .getExecutionErrors()
+ .stream()
+ .map(err -> err.getMessage())
+ .collect(Collectors.joining("\n")));
+ }
+
+ QueryResult result = response.getResult();
+ Iterator> iter = result.iterateAll();
+
+ DateTimeFormatter formatter = ISODateTimeFormat.dateTimeNoMillis().withZoneUTC();
+ while (iter.hasNext()) {
+ List row = iter.next();
+ System.out.printf(
+ "%s\n",
+ formatter.print(
+ new DateTime(
+ // Timestamp values are returned in microseconds since 1970-01-01T00:00:00 UTC,
+ // but org.joda.time.DateTime constructor accepts times in milliseconds.
+ row.get(0).getTimestampValue() / 1000,
+ DateTimeZone.UTC)));
+ }
+ }
+ // [END bigquery_query_params_timestamps]
+}
diff --git a/bigquery/cloud-client/src/test/java/com/example/bigquery/QueryParametersSampleIT.java b/bigquery/cloud-client/src/test/java/com/example/bigquery/QueryParametersSampleIT.java
new file mode 100644
index 00000000000..4577850d872
--- /dev/null
+++ b/bigquery/cloud-client/src/test/java/com/example/bigquery/QueryParametersSampleIT.java
@@ -0,0 +1,71 @@
+/*
+ Copyright 2016 Google Inc.
+
+ 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.example.bigquery;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+/**
+ * Tests for simple app sample.
+ */
+@RunWith(JUnit4.class)
+@SuppressWarnings("checkstyle:abbreviationaswordinname")
+public class QueryParametersSampleIT {
+ private ByteArrayOutputStream bout;
+ private PrintStream out;
+
+ @Before
+ public void setUp() {
+ bout = new ByteArrayOutputStream();
+ out = new PrintStream(bout);
+ System.setOut(out);
+ }
+
+ @After
+ public void tearDown() {
+ System.setOut(null);
+ }
+
+ @Test
+ public void testNamedSample() throws Exception {
+ QueryParametersSample.main(new String[]{"named", "romeoandjuliet", "100"});
+ String got = bout.toString();
+ assertThat(got).contains("love");
+ }
+
+ @Test
+ public void testArraySample() throws Exception {
+ QueryParametersSample.main(new String[]{"array", "M", "WA", "WI", "WV", "WY"});
+ String got = bout.toString();
+ assertThat(got).contains("James");
+ }
+
+ @Test
+ public void testTimestampSample() throws Exception {
+ QueryParametersSample.main(new String[]{"timestamp"});
+ String got = bout.toString();
+ assertThat(got).contains("2016-12-07T09:00:00Z");
+ }
+}