1616
1717package com .google .cloud .spanner .jdbc ;
1818
19- import com .google .cloud .spanner .connection .AbstractStatementParser .ParametersInfo ;
19+ import com .google .cloud .spanner .JdbcDataTypeConverter ;
20+ import com .google .cloud .spanner .ResultSet ;
21+ import com .google .rpc .Code ;
22+ import com .google .spanner .v1 .StructType ;
23+ import com .google .spanner .v1 .StructType .Field ;
24+ import com .google .spanner .v1 .Type ;
25+ import com .google .spanner .v1 .TypeCode ;
2026import java .math .BigDecimal ;
2127import java .sql .Date ;
2228import java .sql .ParameterMetaData ;
2935class JdbcParameterMetaData extends AbstractJdbcWrapper implements ParameterMetaData {
3036 private final JdbcPreparedStatement statement ;
3137
32- JdbcParameterMetaData (JdbcPreparedStatement statement ) throws SQLException {
38+ private final StructType parameters ;
39+
40+ JdbcParameterMetaData (JdbcPreparedStatement statement , ResultSet resultSet ) {
3341 this .statement = statement ;
34- statement .getParameters ().fetchMetaData (statement .getConnection ());
42+ this .parameters = resultSet .getMetadata ().getUndeclaredParameters ();
43+ }
44+
45+ private Field getField (int param ) throws SQLException {
46+ JdbcPreconditions .checkArgument (param > 0 && param <= parameters .getFieldsCount (), param );
47+ String paramName = "p" + param ;
48+ return parameters .getFieldsList ().stream ()
49+ .filter (field -> field .getName ().equals (paramName ))
50+ .findAny ()
51+ .orElseThrow (
52+ () ->
53+ JdbcSqlExceptionFactory .of (
54+ "Unknown parameter: " + paramName , Code .INVALID_ARGUMENT ));
3555 }
3656
3757 @ Override
@@ -41,8 +61,7 @@ public boolean isClosed() {
4161
4262 @ Override
4363 public int getParameterCount () {
44- ParametersInfo info = statement .getParametersInfo ();
45- return info .numberOfParameters ;
64+ return parameters .getFieldsCount ();
4665 }
4766
4867 @ Override
@@ -53,7 +72,7 @@ public int isNullable(int param) {
5372 }
5473
5574 @ Override
56- public boolean isSigned (int param ) {
75+ public boolean isSigned (int param ) throws SQLException {
5776 int type = getParameterType (param );
5877 return type == Types .DOUBLE
5978 || type == Types .FLOAT
@@ -77,9 +96,34 @@ public int getScale(int param) {
7796 }
7897
7998 @ Override
80- public int getParameterType (int param ) {
99+ public int getParameterType (int param ) throws SQLException {
100+ JdbcPreconditions .checkArgument (param > 0 && param <= parameters .getFieldsCount (), param );
101+ int typeFromValue = getParameterTypeFromValue (param );
102+ if (typeFromValue != Types .OTHER ) {
103+ return typeFromValue ;
104+ }
105+
106+ Type type = getField (param ).getType ();
107+ // JDBC only has a generic ARRAY type.
108+ if (type .getCode () == TypeCode .ARRAY ) {
109+ return Types .ARRAY ;
110+ }
111+ JdbcDataType jdbcDataType =
112+ JdbcDataType .getType (JdbcDataTypeConverter .toSpannerType (type ).getCode ());
113+ return jdbcDataType == null ? Types .OTHER : jdbcDataType .getSqlType ();
114+ }
115+
116+ /**
117+ * This method returns the parameter type based on the parameter value that has been set. This was
118+ * previously the only way to get the parameter types of a statement. Cloud Spanner can now return
119+ * the types and names of parameters in a SQL string, which is what this method should return.
120+ */
121+ // TODO: Remove this method for the next major version bump.
122+ private int getParameterTypeFromValue (int param ) {
81123 Integer type = statement .getParameters ().getType (param );
82- if (type != null ) return type ;
124+ if (type != null ) {
125+ return type ;
126+ }
83127
84128 Object value = statement .getParameters ().getParameter (param );
85129 if (value == null ) {
@@ -116,16 +160,49 @@ public int getParameterType(int param) {
116160 }
117161
118162 @ Override
119- public String getParameterTypeName (int param ) {
120- return getSpannerTypeName (getParameterType (param ));
163+ public String getParameterTypeName (int param ) throws SQLException {
164+ JdbcPreconditions .checkArgument (param > 0 && param <= parameters .getFieldsCount (), param );
165+ String typeNameFromValue = getParameterTypeNameFromValue (param );
166+ if (typeNameFromValue != null ) {
167+ return typeNameFromValue ;
168+ }
169+
170+ com .google .cloud .spanner .Type type =
171+ JdbcDataTypeConverter .toSpannerType (getField (param ).getType ());
172+ return getSpannerTypeName (type , statement .getConnection ().getDialect ());
173+ }
174+
175+ private String getParameterTypeNameFromValue (int param ) {
176+ int type = getParameterTypeFromValue (param );
177+ if (type != Types .OTHER ) {
178+ return getSpannerTypeName (type );
179+ }
180+ return null ;
121181 }
122182
123183 @ Override
124- public String getParameterClassName (int param ) {
184+ public String getParameterClassName (int param ) throws SQLException {
185+ JdbcPreconditions .checkArgument (param > 0 && param <= parameters .getFieldsCount (), param );
186+ String classNameFromValue = getParameterClassNameFromValue (param );
187+ if (classNameFromValue != null ) {
188+ return classNameFromValue ;
189+ }
190+
191+ com .google .cloud .spanner .Type type =
192+ JdbcDataTypeConverter .toSpannerType (getField (param ).getType ());
193+ return getClassName (type );
194+ }
195+
196+ // TODO: Remove this method for the next major version bump.
197+ private String getParameterClassNameFromValue (int param ) {
125198 Object value = statement .getParameters ().getParameter (param );
126- if (value != null ) return value .getClass ().getName ();
199+ if (value != null ) {
200+ return value .getClass ().getName ();
201+ }
127202 Integer type = statement .getParameters ().getType (param );
128- if (type != null ) return getClassName (type );
203+ if (type != null ) {
204+ return getClassName (type );
205+ }
129206 return null ;
130207 }
131208
@@ -136,22 +213,26 @@ public int getParameterMode(int param) {
136213
137214 @ Override
138215 public String toString () {
139- StringBuilder res = new StringBuilder ();
140- res .append ("CloudSpannerPreparedStatementParameterMetaData, parameter count: " )
141- .append (getParameterCount ());
142- for (int param = 1 ; param <= getParameterCount (); param ++) {
143- res .append ("\n Parameter " )
144- .append (param )
145- .append (":\n \t Class name: " )
146- .append (getParameterClassName (param ));
147- res .append (",\n \t Parameter type name: " ).append (getParameterTypeName (param ));
148- res .append (",\n \t Parameter type: " ).append (getParameterType (param ));
149- res .append (",\n \t Parameter precision: " ).append (getPrecision (param ));
150- res .append (",\n \t Parameter scale: " ).append (getScale (param ));
151- res .append (",\n \t Parameter signed: " ).append (isSigned (param ));
152- res .append (",\n \t Parameter nullable: " ).append (isNullable (param ));
153- res .append (",\n \t Parameter mode: " ).append (getParameterMode (param ));
216+ try {
217+ StringBuilder res = new StringBuilder ();
218+ res .append ("CloudSpannerPreparedStatementParameterMetaData, parameter count: " )
219+ .append (getParameterCount ());
220+ for (int param = 1 ; param <= getParameterCount (); param ++) {
221+ res .append ("\n Parameter " )
222+ .append (param )
223+ .append (":\n \t Class name: " )
224+ .append (getParameterClassName (param ));
225+ res .append (",\n \t Parameter type name: " ).append (getParameterTypeName (param ));
226+ res .append (",\n \t Parameter type: " ).append (getParameterType (param ));
227+ res .append (",\n \t Parameter precision: " ).append (getPrecision (param ));
228+ res .append (",\n \t Parameter scale: " ).append (getScale (param ));
229+ res .append (",\n \t Parameter signed: " ).append (isSigned (param ));
230+ res .append (",\n \t Parameter nullable: " ).append (isNullable (param ));
231+ res .append (",\n \t Parameter mode: " ).append (getParameterMode (param ));
232+ }
233+ return res .toString ();
234+ } catch (SQLException exception ) {
235+ return "Failed to get parameter metadata: " + exception ;
154236 }
155- return res .toString ();
156237 }
157238}
0 commit comments