Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import com.google.cloud.bigquery.storage.v1.TableName;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.protobuf.Descriptors.DescriptorValidationException;
import java.io.IOException;
Expand Down Expand Up @@ -67,6 +68,7 @@
import java.util.Arrays;
import java.util.Calendar;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

class BigQueryPreparedStatement extends BigQueryStatement implements PreparedStatement {
Expand Down Expand Up @@ -389,15 +391,7 @@ private long bulkInsertWithWriteAPI(BigQueryWriteClient bigQueryWriteClient)
FieldList fieldLists = this.insertSchema.getFields();
if (fieldLists.size() == parameterList.size()) {

JsonObject rowObject = new JsonObject();
for (int j = 0; j < parameterList.size(); j++) {
BigQueryJdbcParameter parameter = parameterList.get(j);
if (parameter.getSqlType() == StandardSQLTypeName.STRING) {
rowObject.addProperty(fieldLists.get(j).getName(), parameter.getValue().toString());
} else {
rowObject.addProperty(fieldLists.get(j).getName(), gson.toJson(parameter.getValue()));
}
}
JsonObject rowObject = createJsonRow(fieldLists, parameterList, gson);
jsonArray.add(rowObject);

if (jsonArray.size() == this.querySettings.getWriteAPIAppendRowCount()
Expand Down Expand Up @@ -431,6 +425,22 @@ private long bulkInsertWithWriteAPI(BigQueryWriteClient bigQueryWriteClient)
return rowCount;
}

static JsonObject createJsonRow(
FieldList fieldLists, List<BigQueryJdbcParameter> parameterList, Gson gson) {
JsonObject rowObject = new JsonObject();
for (int j = 0; j < parameterList.size(); j++) {
BigQueryJdbcParameter parameter = parameterList.get(j);
if (parameter.getValue() == null) {
rowObject.add(fieldLists.get(j).getName(), JsonNull.INSTANCE);
} else if (parameter.getSqlType() == StandardSQLTypeName.STRING) {
rowObject.addProperty(fieldLists.get(j).getName(), parameter.getValue().toString());
} else {
rowObject.addProperty(fieldLists.get(j).getName(), gson.toJson(parameter.getValue()));
}
}
return rowObject;
}

private void setInsertMetadata(QueryStatistics statistics) throws SQLException {
LOG.finer("++enter++");
if (!statistics.getStatementType().equals(StatementType.INSERT)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,18 @@
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import com.google.cloud.bigquery.Field;
import com.google.cloud.bigquery.FieldList;
import com.google.cloud.bigquery.QueryJobConfiguration;
import com.google.cloud.bigquery.Schema;
import com.google.cloud.bigquery.StandardSQLTypeName;
import com.google.gson.Gson;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import java.sql.Array;
import java.sql.Date;
import java.sql.ParameterMetaData;
Expand All @@ -38,6 +45,7 @@
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.TimeZone;
import org.junit.jupiter.api.BeforeEach;
Expand All @@ -51,6 +59,7 @@ public class BigQueryPreparedStatementSettersTest {
@BeforeEach
public void setUp() throws Exception {
connection = mock(BigQueryConnection.class);
when(connection.getQueryDialect()).thenReturn("SQL");
preparedStatement = new BigQueryPreparedStatement(connection, "SELECT ?, ?, ?, ?, ?");
}

Expand Down Expand Up @@ -269,4 +278,51 @@ public void testSetObjectWithJavaTime() throws Exception {
preparedStatement.setObject(4, instant);
assertEquals(Timestamp.class, preparedStatement.parameterHandler.getType(4));
}

// Verifies standard DML QueryJobConfiguration creates positional parameters with null values when
// setObject is called with null.
@Test
public void testBatchJobConfigurationWithSetObjectNull() throws Exception {
preparedStatement.setObject(1, null);
preparedStatement.setInt(2, 42);
preparedStatement.setString(3, "test");
preparedStatement.setDouble(4, 3.14);
preparedStatement.setBoolean(5, true);
preparedStatement.addBatch();

ArrayList<BigQueryJdbcParameter> batchParams =
preparedStatement.parameterHandler.parametersList;
QueryJobConfiguration config = preparedStatement.getWriteBatchJobConfiguration(batchParams);
assertNotNull(config);
assertEquals(5, config.getPositionalParameters().size());
assertNull(config.getPositionalParameters().get(0).getValue());
assertEquals(StandardSQLTypeName.STRING, config.getPositionalParameters().get(0).getType());
}

// Verifies Storage Write API JSON row serialization maps null parameter values to
// JsonNull.INSTANCE.
@Test
public void testCreateJsonRowWithSetObjectNull() throws Exception {
preparedStatement.setObject(1, null);
preparedStatement.setInt(2, 42);
preparedStatement.setString(3, "test");
preparedStatement.setDouble(4, 3.14);
preparedStatement.setBoolean(5, true);

FieldList fieldList =
FieldList.of(
Field.of("col1", StandardSQLTypeName.STRING),
Field.of("col2", StandardSQLTypeName.INT64),
Field.of("col3", StandardSQLTypeName.STRING),
Field.of("col4", StandardSQLTypeName.FLOAT64),
Field.of("col5", StandardSQLTypeName.BOOL));

ArrayList<BigQueryJdbcParameter> params = preparedStatement.parameterHandler.parametersList;
JsonObject jsonRow = BigQueryPreparedStatement.createJsonRow(fieldList, params, new Gson());

assertNotNull(jsonRow);
assertEquals(JsonNull.INSTANCE, jsonRow.get("col1"));
assertTrue(jsonRow.get("col1").isJsonNull());
assertEquals("42", jsonRow.get("col2").getAsString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1197,6 +1197,57 @@ public void testBulkInsertOperation() throws SQLException {
}
}

// Verifies batch inserts via BigQuery Storage Write API succeed when parameters are bound to
// null.
@Test
public void testBulkInsertOperationWithSetObjectNull() throws SQLException {
String TABLE_NAME = "JDBC_BULK_INSERT_NULL_TABLE_" + randomNumber;
String createQuery =
String.format(
"CREATE OR REPLACE TABLE %s.%s (`StringField` STRING,\n"
+ " `IntegerField` INTEGER,"
+ " `FloatField` FLOAT64,"
+ " `NumericField` NUMERIC,"
+ " `BigNumericField` BIGNUMERIC,"
+ " `BooleanField` BOOLEAN"
+ " );",
DATASET, TABLE_NAME);
String insertQuery =
String.format("INSERT INTO %s.%s VALUES(?, ?, ?, ?, ?, ?);", DATASET, TABLE_NAME);
String dropQuery = String.format("DROP TABLE %s.%s", DATASET, TABLE_NAME);
String selectQuery = String.format("SELECT * FROM %s.%s", DATASET, TABLE_NAME);

String connection_uri =
ITNightlyBigQueryTest.connection_uri
+ "EnableWriteAPI=1;"
+ "SWA_ActivationRowCount=5;"
+ "SWA_AppendRowCount=500";

try (Connection connection = DriverManager.getConnection(connection_uri)) {
bigQueryStatement.execute(createQuery);
PreparedStatement statement = connection.prepareStatement(insertQuery);
for (int i = 0; i < 20; ++i) {
statement.setObject(1, null);
statement.setInt(2, i);
statement.setFloat(3, (float) (i + .6));
statement.setInt(4, random.nextInt());
statement.setInt(5, random.nextInt());
statement.setBoolean(6, true);

statement.addBatch();
}
int[] result = statement.executeBatch();

ResultSet resultSet = bigQueryStatement.executeQuery(selectQuery);
assertEquals(result.length, resultSetRowCount(resultSet));

bigQueryStatement.execute(dropQuery);

} catch (SQLException e) {
throw new BigQueryJdbcException(e);
}
}

@Test
public void testBulkInsertOperationStandard() throws SQLException {
String TABLE_NAME = "JDBC_BULK_INSERT_STANDARD_TABLE_" + randomNumber;
Expand Down
Loading