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 @@ -28,6 +28,12 @@
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZonedDateTime;
import java.util.AbstractMap.SimpleEntry;
import java.util.Map;

Expand Down Expand Up @@ -117,11 +123,15 @@ static StandardSQLTypeName classToType(Class type)
return StandardSQLTypeName.FLOAT64;
} else if (BigDecimal.class.isAssignableFrom(type)) {
return StandardSQLTypeName.NUMERIC;
} else if (Date.class.isAssignableFrom(type)) {
} else if (Date.class.isAssignableFrom(type) || LocalDate.class.isAssignableFrom(type)) {
return StandardSQLTypeName.DATE;
} else if (Timestamp.class.isAssignableFrom(type)) {
} else if (Timestamp.class.isAssignableFrom(type)
|| LocalDateTime.class.isAssignableFrom(type)
|| OffsetDateTime.class.isAssignableFrom(type)
|| Instant.class.isAssignableFrom(type)
|| ZonedDateTime.class.isAssignableFrom(type)) {
return StandardSQLTypeName.TIMESTAMP;
} else if (Time.class.isAssignableFrom(type)) {
} else if (Time.class.isAssignableFrom(type) || LocalTime.class.isAssignableFrom(type)) {
return StandardSQLTypeName.TIME;
} else if (JsonObject.class.isAssignableFrom(type)) {
return StandardSQLTypeName.JSON;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/*
* Copyright 2026 Google LLC
*
* 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
*
* https://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.cloud.bigquery.jdbc;

import com.google.cloud.bigquery.StandardSQLTypeName;
import com.google.cloud.bigquery.exception.BigQueryJdbcException;
import java.sql.ParameterMetaData;
import java.sql.SQLException;
import java.sql.Types;

/** ParameterMetaData implementation for BigQuery JDBC PreparedStatement parameters. */
class BigQueryParameterMetaData implements ParameterMetaData {
private final int parameterCount;
private final BigQueryParameterHandler parameterHandler;

BigQueryParameterMetaData(int parameterCount, BigQueryParameterHandler parameterHandler) {
this.parameterCount = parameterCount;
this.parameterHandler = parameterHandler;
}

private void checkValidIndex(int param) throws SQLException {
if (param < 1 || param > this.parameterCount) {
throw new BigQueryJdbcException("Invalid parameter index: " + param);
}
}

@Override
public int getParameterCount() {
return this.parameterCount;
}

@Override
public int isNullable(int param) throws SQLException {
checkValidIndex(param);
return parameterNullable;
}

@Override
public boolean isSigned(int param) throws SQLException {
int type = getParameterType(param);
return type == Types.SMALLINT
|| type == Types.TINYINT
|| type == Types.INTEGER
|| type == Types.BIGINT
|| type == Types.FLOAT
|| type == Types.DOUBLE
|| type == Types.DECIMAL
|| type == Types.NUMERIC;
}

@Override
public int getPrecision(int param) throws SQLException {
checkValidIndex(param);
StandardSQLTypeName sqlType = getStandardSQLTypeName(param);
if (sqlType != null) {
BigQueryJdbcTypeMappings.ColumnTypeInfo typeInfo =
BigQueryJdbcTypeMappings.STANDARD_TYPE_INFO.get(sqlType);
if (typeInfo != null && typeInfo.columnSize != null) {
return typeInfo.columnSize;
}
}
return 0;
}

@Override
public int getScale(int param) throws SQLException {
checkValidIndex(param);
StandardSQLTypeName sqlType = getStandardSQLTypeName(param);
if (sqlType != null) {
BigQueryJdbcTypeMappings.ColumnTypeInfo typeInfo =
BigQueryJdbcTypeMappings.STANDARD_TYPE_INFO.get(sqlType);
if (typeInfo != null && typeInfo.decimalDigits != null) {
return typeInfo.decimalDigits;
}
}
return 0;
}

@Override
public int getParameterType(int param) throws SQLException {
checkValidIndex(param);
StandardSQLTypeName sqlType = getStandardSQLTypeName(param);
if (sqlType != null) {
Integer jdbcType = BigQueryJdbcTypeMappings.standardSQLToJavaSqlTypesMapping.get(sqlType);
if (jdbcType != null) {
return jdbcType;
}
}
return Types.OTHER;
}

@Override
public String getParameterTypeName(int param) throws SQLException {
checkValidIndex(param);
StandardSQLTypeName sqlType = getStandardSQLTypeName(param);
return sqlType != null ? sqlType.name() : "UNKNOWN";
}

@Override
public String getParameterClassName(int param) throws SQLException {
checkValidIndex(param);
StandardSQLTypeName sqlType = getStandardSQLTypeName(param);
if (sqlType != null) {
Class<?> clazz = BigQueryJdbcTypeMappings.standardSQLToJavaTypeMapping.get(sqlType);
if (clazz != null) {
return clazz.getName();
}
}
Class<?> boundType = this.parameterHandler.getType(param);
if (boundType != null) {
return boundType.getName();
}
return Object.class.getName();
}

@Override
public int getParameterMode(int param) throws SQLException {
checkValidIndex(param);
return parameterModeIn;
}

private StandardSQLTypeName getStandardSQLTypeName(int param) {
if (this.parameterHandler != null) {
StandardSQLTypeName sqlType = this.parameterHandler.getSqlType(param);
if (sqlType != null) {
return sqlType;
}
Class<?> javaType = this.parameterHandler.getType(param);
if (javaType != null) {
try {
return BigQueryJdbcTypeMappings.classToType(javaType);
} catch (SQLException ignored) {
// fall back to default
}
}
}
return null;
}

@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
if (iface.isInstance(this)) {
return iface.cast(this);
}
throw new BigQueryJdbcException("Cannot unwrap to " + iface.getName());
}

@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return iface != null && iface.isInstance(this);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
Expand All @@ -70,7 +76,7 @@ class BigQueryPreparedStatement extends BigQueryStatement implements PreparedSta
protected int parameterCount = 0;
protected String currentQuery;
private Queue<ArrayList<BigQueryJdbcParameter>> batchParameters = new LinkedList<>();
private Schema insertSchema = null;
Schema insertSchema = null;
private TableName insertTableName = null;

BigQueryPreparedStatement(BigQueryConnection connection, String query) {
Expand Down Expand Up @@ -230,6 +236,30 @@ public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQ
setNull(parameterIndex, targetSqlType);
return;
}
if (x instanceof LocalDate) {
setDate(parameterIndex, Date.valueOf((LocalDate) x));
return;
}
if (x instanceof LocalTime) {
setTime(parameterIndex, Time.valueOf((LocalTime) x));
return;
}
if (x instanceof LocalDateTime) {
setTimestamp(parameterIndex, Timestamp.valueOf((LocalDateTime) x));
return;
}
if (x instanceof OffsetDateTime) {
setTimestamp(parameterIndex, Timestamp.from(((OffsetDateTime) x).toInstant()));
return;
}
if (x instanceof Instant) {
setTimestamp(parameterIndex, Timestamp.from((Instant) x));
return;
}
if (x instanceof ZonedDateTime) {
setTimestamp(parameterIndex, Timestamp.from(((ZonedDateTime) x).toInstant()));
return;
}
Class<?> javaType = BigQueryJdbcTypeMappings.getJavaType(targetSqlType);
this.parameterHandler.setParameter(parameterIndex, x, javaType);
}
Expand All @@ -241,6 +271,30 @@ public void setObject(int parameterIndex, Object x) throws SQLException {
setNull(parameterIndex, Types.NULL);
return;
}
if (x instanceof LocalDate) {
setDate(parameterIndex, Date.valueOf((LocalDate) x));
return;
}
if (x instanceof LocalTime) {
setTime(parameterIndex, Time.valueOf((LocalTime) x));
return;
}
if (x instanceof LocalDateTime) {
setTimestamp(parameterIndex, Timestamp.valueOf((LocalDateTime) x));
return;
}
if (x instanceof OffsetDateTime) {
setTimestamp(parameterIndex, Timestamp.from(((OffsetDateTime) x).toInstant()));
return;
}
if (x instanceof Instant) {
setTimestamp(parameterIndex, Timestamp.from((Instant) x));
return;
}
if (x instanceof ZonedDateTime) {
setTimestamp(parameterIndex, Timestamp.from(((ZonedDateTime) x).toInstant()));
return;
}
this.parameterHandler.setParameter(parameterIndex, x, x.getClass());
}

Expand Down Expand Up @@ -470,24 +524,83 @@ public void setArray(int parameterIndex, Array x) throws SQLException {
}

@Override
public ResultSetMetaData getMetaData() {
// TODO(neenu) :IMPLEMENT metadata
public ResultSetMetaData getMetaData() throws SQLException {
checkClosed();
if (this.insertSchema != null) {
return BigQueryResultSetMetadata.of(this.insertSchema.getFields(), this);
}
return null;
}

@Override
public void setDate(int parameterIndex, Date x, Calendar cal) {
// TODO :NOT IMPLEMENTED
public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException {
checkClosed();
if (x == null) {
setNull(parameterIndex, Types.DATE);
return;
}
if (cal == null) {
setDate(parameterIndex, x);
return;
}
Calendar targetCal = (Calendar) cal.clone();
Calendar defaultCal = Calendar.getInstance();
defaultCal.setTime(x);
targetCal.set(Calendar.YEAR, defaultCal.get(Calendar.YEAR));
targetCal.set(Calendar.MONTH, defaultCal.get(Calendar.MONTH));
targetCal.set(Calendar.DAY_OF_MONTH, defaultCal.get(Calendar.DAY_OF_MONTH));
targetCal.set(Calendar.HOUR_OF_DAY, 0);
targetCal.set(Calendar.MINUTE, 0);
targetCal.set(Calendar.SECOND, 0);
targetCal.set(Calendar.MILLISECOND, 0);
setDate(parameterIndex, new Date(targetCal.getTimeInMillis()));
}

@Override
public void setTime(int parameterIndex, Time x, Calendar cal) {
// TODO :NOT IMPLEMENTED
public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException {
checkClosed();
if (x == null) {
setNull(parameterIndex, Types.TIME);
return;
}
if (cal == null) {
setTime(parameterIndex, x);
return;
}
Calendar targetCal = (Calendar) cal.clone();
Calendar defaultCal = Calendar.getInstance();
defaultCal.setTime(x);
targetCal.set(Calendar.HOUR_OF_DAY, defaultCal.get(Calendar.HOUR_OF_DAY));
targetCal.set(Calendar.MINUTE, defaultCal.get(Calendar.MINUTE));
targetCal.set(Calendar.SECOND, defaultCal.get(Calendar.SECOND));
targetCal.set(Calendar.MILLISECOND, defaultCal.get(Calendar.MILLISECOND));
setTime(parameterIndex, new Time(targetCal.getTimeInMillis()));
}

@Override
public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) {
// TODO :NOT IMPLEMENTED
public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException {
checkClosed();
if (x == null) {
setNull(parameterIndex, Types.TIMESTAMP);
return;
}
if (cal == null) {
setTimestamp(parameterIndex, x);
return;
}
Calendar targetCal = (Calendar) cal.clone();
Calendar defaultCal = Calendar.getInstance();
defaultCal.setTime(x);
targetCal.set(Calendar.YEAR, defaultCal.get(Calendar.YEAR));
targetCal.set(Calendar.MONTH, defaultCal.get(Calendar.MONTH));
targetCal.set(Calendar.DAY_OF_MONTH, defaultCal.get(Calendar.DAY_OF_MONTH));
targetCal.set(Calendar.HOUR_OF_DAY, defaultCal.get(Calendar.HOUR_OF_DAY));
targetCal.set(Calendar.MINUTE, defaultCal.get(Calendar.MINUTE));
targetCal.set(Calendar.SECOND, defaultCal.get(Calendar.SECOND));
targetCal.set(Calendar.MILLISECOND, defaultCal.get(Calendar.MILLISECOND));
Timestamp adjustedTimestamp = new Timestamp(targetCal.getTimeInMillis());
adjustedTimestamp.setNanos(x.getNanos());
setTimestamp(parameterIndex, adjustedTimestamp);
Comment thread
Neenu1995 marked this conversation as resolved.
}

@Override
Expand All @@ -501,9 +614,9 @@ public void setURL(int parameterIndex, URL x) throws SQLException {
}

@Override
public ParameterMetaData getParameterMetaData() {
// TODO(neenu) :IMPLEMENT
return null;
public ParameterMetaData getParameterMetaData() throws SQLException {
checkClosed();
return new BigQueryParameterMetaData(this.parameterCount, this.parameterHandler);
}

@Override
Expand Down
Loading
Loading