Skip to content

Commit 3f89f99

Browse files
committed
feat(bigquery-jdbc): implement parameter setters in PreparedStatement
1 parent b003e0b commit 3f89f99

4 files changed

Lines changed: 190 additions & 32 deletions

File tree

java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcTypeMappings.java

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ class BigQueryJdbcTypeMappings {
8080
ImmutableMap.ofEntries(
8181
entry(Types.BIGINT, Long.class),
8282
entry(Types.INTEGER, Integer.class),
83+
entry(Types.SMALLINT, Long.class),
84+
entry(Types.TINYINT, Long.class),
8385
entry(Types.BOOLEAN, Boolean.class),
8486
entry(Types.DOUBLE, Double.class),
8587
entry(Types.FLOAT, Float.class),
@@ -94,38 +96,33 @@ class BigQueryJdbcTypeMappings {
9496
entry(Types.VARBINARY, byte[].class),
9597
entry(Types.STRUCT, Struct.class),
9698
entry(Types.BIT, Boolean.class),
97-
entry(Types.ARRAY, Array.class));
99+
entry(Types.ARRAY, Array.class),
100+
entry(Types.NULL, String.class));
98101

99102
static StandardSQLTypeName classToType(Class type)
100103
throws BigQueryJdbcSqlFeatureNotSupportedException {
101104
if (Boolean.class.isAssignableFrom(type)) {
102105
return StandardSQLTypeName.BOOL;
103106
} else if (String.class.isAssignableFrom(type)) {
104107
return StandardSQLTypeName.STRING;
105-
} else if (String.class.isAssignableFrom(type)) {
106-
return StandardSQLTypeName.GEOGRAPHY;
107-
} else if (String.class.isAssignableFrom(type)) {
108-
return StandardSQLTypeName.DATETIME;
109108
} else if (Integer.class.isAssignableFrom(type)) {
110109
return StandardSQLTypeName.INT64;
111110
} else if (Long.class.isAssignableFrom(type)) {
112111
return StandardSQLTypeName.INT64;
112+
} else if (Short.class.isAssignableFrom(type)) {
113+
return StandardSQLTypeName.INT64;
113114
} else if (Double.class.isAssignableFrom(type)) {
114115
return StandardSQLTypeName.FLOAT64;
115116
} else if (Float.class.isAssignableFrom(type)) {
116117
return StandardSQLTypeName.FLOAT64;
117118
} else if (BigDecimal.class.isAssignableFrom(type)) {
118119
return StandardSQLTypeName.NUMERIC;
119-
} else if (BigDecimal.class.isAssignableFrom(type)) {
120-
return StandardSQLTypeName.BIGNUMERIC;
121120
} else if (Date.class.isAssignableFrom(type)) {
122121
return StandardSQLTypeName.DATE;
123122
} else if (Timestamp.class.isAssignableFrom(type)) {
124123
return StandardSQLTypeName.TIMESTAMP;
125124
} else if (Time.class.isAssignableFrom(type)) {
126125
return StandardSQLTypeName.TIME;
127-
} else if (String.class.isAssignableFrom(type)) {
128-
return StandardSQLTypeName.JSON;
129126
} else if (JsonObject.class.isAssignableFrom(type)) {
130127
return StandardSQLTypeName.JSON;
131128
} else if (Byte.class.isAssignableFrom(type)) {

java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryPreparedStatement.java

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import java.sql.SQLXML;
5656
import java.sql.Time;
5757
import java.sql.Timestamp;
58+
import java.sql.Types;
5859
import java.util.ArrayList;
5960
import java.util.Arrays;
6061
import java.util.Calendar;
@@ -114,8 +115,15 @@ public void clearParameters() {
114115
}
115116

116117
@Override
117-
public void setNull(int parameterIndex, int sqlType) {
118-
// TODO(neenu): implement null case
118+
public void setNull(int parameterIndex, int sqlType) throws SQLException {
119+
checkClosed();
120+
Class<?> javaType;
121+
try {
122+
javaType = BigQueryJdbcTypeMappings.getJavaType(sqlType);
123+
} catch (SQLException e) {
124+
javaType = String.class;
125+
}
126+
this.parameterHandler.setParameter(parameterIndex, null, javaType);
119127
}
120128

121129
@Override
@@ -131,8 +139,9 @@ public void setByte(int parameterIndex, byte x) throws SQLException {
131139
}
132140

133141
@Override
134-
public void setShort(int parameterIndex, short x) {
135-
// TODO(neenu): implement Bytes conversion.
142+
public void setShort(int parameterIndex, short x) throws SQLException {
143+
checkClosed();
144+
this.parameterHandler.setParameter(parameterIndex, (long) x, Long.class);
136145
}
137146

138147
@Override
@@ -172,8 +181,9 @@ public void setString(int parameterIndex, String x) throws SQLException {
172181
}
173182

174183
@Override
175-
public void setBytes(int parameterIndex, byte[] x) {
176-
// TODO(neenu): implement Bytes conversion.
184+
public void setBytes(int parameterIndex, byte[] x) throws SQLException {
185+
checkClosed();
186+
this.parameterHandler.setParameter(parameterIndex, x, byte[].class);
177187
}
178188

179189
@Override
@@ -218,11 +228,24 @@ public void setBinaryStream(int parameterIndex, InputStream x, int length) {
218228
}
219229

220230
@Override
221-
public void setObject(int parameterIndex, Object x, int targetSqlType) {}
231+
public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException {
232+
checkClosed();
233+
if (x == null) {
234+
setNull(parameterIndex, targetSqlType);
235+
return;
236+
}
237+
Class<?> javaType = BigQueryJdbcTypeMappings.getJavaType(targetSqlType);
238+
this.parameterHandler.setParameter(parameterIndex, x, javaType);
239+
}
222240

223241
@Override
224-
public void setObject(int parameterIndex, Object x) {
225-
// TODO :NOT IMPLEMENTED
242+
public void setObject(int parameterIndex, Object x) throws SQLException {
243+
checkClosed();
244+
if (x == null) {
245+
setNull(parameterIndex, Types.NULL);
246+
return;
247+
}
248+
this.parameterHandler.setParameter(parameterIndex, x, x.getClass());
226249
}
227250

228251
@Override
@@ -444,8 +467,9 @@ public void setClob(int parameterIndex, Clob x) {
444467
}
445468

446469
@Override
447-
public void setArray(int parameterIndex, Array x) {
448-
// TODO(neenu) :IMPLEMENT ARRAY
470+
public void setArray(int parameterIndex, Array x) throws SQLException {
471+
checkClosed();
472+
this.parameterHandler.setParameter(parameterIndex, x, Array.class);
449473
}
450474

451475
@Override
@@ -470,8 +494,8 @@ public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) {
470494
}
471495

472496
@Override
473-
public void setNull(int parameterIndex, int sqlType, String typeName) {
474-
// TODO :NOT IMPLEMENTED
497+
public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException {
498+
setNull(parameterIndex, sqlType);
475499
}
476500

477501
@Override
@@ -526,8 +550,10 @@ public void setSQLXML(int parameterIndex, SQLXML xmlObject) {
526550
}
527551

528552
@Override
529-
public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) {
530-
// TODO(neenu) : IMPLEMENT?
553+
public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength)
554+
throws SQLException {
555+
checkClosed();
556+
setObject(parameterIndex, x, targetSqlType);
531557
}
532558

533559
@Override
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.bigquery.jdbc;
18+
19+
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
20+
import static org.junit.jupiter.api.Assertions.assertEquals;
21+
import static org.junit.jupiter.api.Assertions.assertNull;
22+
import static org.mockito.Mockito.mock;
23+
24+
import com.google.cloud.bigquery.StandardSQLTypeName;
25+
import java.sql.Array;
26+
import java.sql.Types;
27+
import org.junit.jupiter.api.BeforeEach;
28+
import org.junit.jupiter.api.Test;
29+
30+
public class BigQueryPreparedStatementSettersTest {
31+
32+
private BigQueryConnection connection;
33+
private BigQueryPreparedStatement preparedStatement;
34+
35+
@BeforeEach
36+
public void setUp() throws Exception {
37+
connection = mock(BigQueryConnection.class);
38+
preparedStatement = new BigQueryPreparedStatement(connection, "SELECT ?, ?, ?, ?, ?");
39+
}
40+
41+
@Test
42+
public void testSetNullWithSqlType() throws Exception {
43+
preparedStatement.setNull(1, Types.VARCHAR);
44+
assertNull(preparedStatement.parameterHandler.getParameter(1));
45+
assertEquals(String.class, preparedStatement.parameterHandler.getType(1));
46+
assertEquals(StandardSQLTypeName.STRING, preparedStatement.parameterHandler.getSqlType(1));
47+
48+
preparedStatement.setNull(2, Types.INTEGER);
49+
assertNull(preparedStatement.parameterHandler.getParameter(2));
50+
assertEquals(Integer.class, preparedStatement.parameterHandler.getType(2));
51+
assertEquals(StandardSQLTypeName.INT64, preparedStatement.parameterHandler.getSqlType(2));
52+
53+
preparedStatement.setNull(3, Types.NULL);
54+
assertNull(preparedStatement.parameterHandler.getParameter(3));
55+
assertEquals(String.class, preparedStatement.parameterHandler.getType(3));
56+
assertEquals(StandardSQLTypeName.STRING, preparedStatement.parameterHandler.getSqlType(3));
57+
}
58+
59+
@Test
60+
public void testSetNullWithTypeName() throws Exception {
61+
preparedStatement.setNull(1, Types.VARCHAR, "VARCHAR");
62+
assertNull(preparedStatement.parameterHandler.getParameter(1));
63+
assertEquals(String.class, preparedStatement.parameterHandler.getType(1));
64+
assertEquals(StandardSQLTypeName.STRING, preparedStatement.parameterHandler.getSqlType(1));
65+
}
66+
67+
@Test
68+
public void testSetShort() throws Exception {
69+
preparedStatement.setShort(1, (short) 42);
70+
assertEquals(42L, preparedStatement.parameterHandler.getParameter(1));
71+
assertEquals(Long.class, preparedStatement.parameterHandler.getType(1));
72+
assertEquals(StandardSQLTypeName.INT64, preparedStatement.parameterHandler.getSqlType(1));
73+
}
74+
75+
@Test
76+
public void testSetBytes() throws Exception {
77+
byte[] bytes = new byte[] {1, 2, 3, 4};
78+
preparedStatement.setBytes(1, bytes);
79+
assertArrayEquals(bytes, (byte[]) preparedStatement.parameterHandler.getParameter(1));
80+
assertEquals(byte[].class, preparedStatement.parameterHandler.getType(1));
81+
assertEquals(StandardSQLTypeName.BYTES, preparedStatement.parameterHandler.getSqlType(1));
82+
}
83+
84+
@Test
85+
public void testSetObjectSingleArg() throws Exception {
86+
preparedStatement.setObject(1, "hello");
87+
assertEquals("hello", preparedStatement.parameterHandler.getParameter(1));
88+
assertEquals(String.class, preparedStatement.parameterHandler.getType(1));
89+
assertEquals(StandardSQLTypeName.STRING, preparedStatement.parameterHandler.getSqlType(1));
90+
91+
preparedStatement.setObject(2, 100L);
92+
assertEquals(100L, preparedStatement.parameterHandler.getParameter(2));
93+
assertEquals(Long.class, preparedStatement.parameterHandler.getType(2));
94+
assertEquals(StandardSQLTypeName.INT64, preparedStatement.parameterHandler.getSqlType(2));
95+
96+
preparedStatement.setObject(3, null);
97+
assertNull(preparedStatement.parameterHandler.getParameter(3));
98+
assertEquals(String.class, preparedStatement.parameterHandler.getType(3));
99+
}
100+
101+
@Test
102+
public void testSetObjectWithTargetSqlType() throws Exception {
103+
preparedStatement.setObject(1, 123, Types.INTEGER);
104+
assertEquals(123, preparedStatement.parameterHandler.getParameter(1));
105+
assertEquals(Integer.class, preparedStatement.parameterHandler.getType(1));
106+
assertEquals(StandardSQLTypeName.INT64, preparedStatement.parameterHandler.getSqlType(1));
107+
108+
preparedStatement.setObject(2, null, Types.INTEGER);
109+
assertNull(preparedStatement.parameterHandler.getParameter(2));
110+
assertEquals(Integer.class, preparedStatement.parameterHandler.getType(2));
111+
112+
preparedStatement.setObject(3, 456, Types.INTEGER, 0);
113+
assertEquals(456, preparedStatement.parameterHandler.getParameter(3));
114+
assertEquals(Integer.class, preparedStatement.parameterHandler.getType(3));
115+
}
116+
117+
@Test
118+
public void testSetArray() throws Exception {
119+
Array mockArray = mock(Array.class);
120+
preparedStatement.setArray(1, mockArray);
121+
assertEquals(mockArray, preparedStatement.parameterHandler.getParameter(1));
122+
assertEquals(Array.class, preparedStatement.parameterHandler.getType(1));
123+
assertEquals(StandardSQLTypeName.ARRAY, preparedStatement.parameterHandler.getSqlType(1));
124+
}
125+
}

java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITBigQueryJDBCTest.java

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import java.sql.Statement;
5252
import java.sql.Time;
5353
import java.sql.Timestamp;
54+
import java.sql.Types;
5455
import java.time.LocalTime;
5556
import java.util.Properties;
5657
import java.util.Random;
@@ -984,11 +985,11 @@ public void testPreparedExecuteMethod() throws SQLException {
984985
String TABLE_NAME = "JDBC_PREPARED_EXECUTE_TABLE_" + randomNumber;
985986
String createQuery =
986987
String.format(
987-
"CREATE OR REPLACE TABLE %s.%s (`StringField` STRING, `IntegerField` INTEGER);",
988+
"CREATE OR REPLACE TABLE %s.%s (`StringField` STRING, `IntegerField` INTEGER, `ShortField` INT64, `BytesField` BYTES, `DoubleField` FLOAT64, `BooleanField` BOOL, `NullField` STRING);",
988989
DATASET, TABLE_NAME);
989990
String insertQuery =
990991
String.format(
991-
"INSERT INTO %s.%s (StringField, IntegerField) VALUES (?,?), (?,?), (?,?), (?,?);",
992+
"INSERT INTO %s.%s (StringField, IntegerField, ShortField, BytesField, DoubleField, BooleanField, NullField) VALUES (?,?,?,?,?,?,?), (?,?,?,?,?,?,?);",
992993
DATASET, TABLE_NAME);
993994
String updateQuery =
994995
String.format("UPDATE %s.%s SET StringField=? WHERE IntegerField=?", DATASET, TABLE_NAME);
@@ -999,14 +1000,23 @@ public void testPreparedExecuteMethod() throws SQLException {
9991000
assertFalse(createStatus);
10001001

10011002
PreparedStatement insertStmt = bigQueryConnection.prepareStatement(insertQuery);
1003+
// Row 1: Testing setString, setInt, setShort, setBytes, setObject, setNull
10021004
insertStmt.setString(1, "String1");
10031005
insertStmt.setInt(2, 111);
1004-
insertStmt.setString(3, "String2");
1005-
insertStmt.setInt(4, 222);
1006-
insertStmt.setString(5, "String3");
1007-
insertStmt.setInt(6, 333);
1008-
insertStmt.setString(7, "String4");
1009-
insertStmt.setInt(8, 444);
1006+
insertStmt.setShort(3, (short) 12);
1007+
insertStmt.setBytes(4, new byte[] {0x1, 0x2});
1008+
insertStmt.setObject(5, 3.14d);
1009+
insertStmt.setObject(6, true, Types.BOOLEAN);
1010+
insertStmt.setNull(7, Types.VARCHAR);
1011+
1012+
// Row 2: Testing setString, setInt, setShort, setBytes, setObject, setNull with typeName
1013+
insertStmt.setString(8, "String2");
1014+
insertStmt.setInt(9, 222);
1015+
insertStmt.setShort(10, (short) 34);
1016+
insertStmt.setBytes(11, new byte[] {0x3, 0x4});
1017+
insertStmt.setObject(12, 6.28d);
1018+
insertStmt.setObject(13, false);
1019+
insertStmt.setNull(14, Types.VARCHAR, "STRING");
10101020

10111021
boolean insertStatus = insertStmt.execute();
10121022
assertFalse(insertStatus);

0 commit comments

Comments
 (0)