Skip to content

Commit d1f85ca

Browse files
author
diego Dupin
committed
[CONJ-705] parameter metadata get parameter count even when query cannot be prepared
1 parent 04f0062 commit d1f85ca

File tree

4 files changed

+276
-18
lines changed

4 files changed

+276
-18
lines changed

src/main/java/org/mariadb/jdbc/ClientPreparedStatement.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -401,10 +401,14 @@ public ResultSetMetaData getMetaData() throws SQLException {
401401
* @since 1.4
402402
*/
403403
@Override
404-
public ParameterMetaData getParameterMetaData() throws SQLException {
404+
public java.sql.ParameterMetaData getParameterMetaData() throws SQLException {
405405
// send COM_STMT_PREPARE
406406
if (prepareResult == null) {
407-
con.getClient().execute(new PreparePacket(escapeTimeout(sql)), this);
407+
try {
408+
con.getClient().execute(new PreparePacket(escapeTimeout(sql)), this);
409+
} catch (SQLException e) {
410+
return new SimpleParameterMetaData(exceptionFactory(), parser.getParamCount());
411+
}
408412
}
409413
return new ParameterMetaData(exceptionFactory(), prepareResult.getParameters());
410414
}

src/main/java/org/mariadb/jdbc/ParameterMetaData.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,13 @@ public int getScale(int idx) throws SQLException {
104104
* Retrieves the designated parameter's SQL type. Parameter type are not sent by server. See
105105
* https://jira.mariadb.org/browse/CONJ-568 and https://jira.mariadb.org/browse/MDEV-15031
106106
*
107-
* @param param the first parameter is 1, the second is 2, ...
107+
* @param idx the first parameter is 1, the second is 2, ...
108108
* @return SQL types from <code>java.sql.Types</code>
109109
* @throws SQLException because not supported
110110
*/
111111
@Override
112-
public int getParameterType(int param) throws SQLException {
112+
public int getParameterType(int idx) throws SQLException {
113+
checkIndex(idx);
113114
throw exceptionFactory.create("Getting parameter type metadata are not supported", "0A000", -1);
114115
}
115116

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
// SPDX-License-Identifier: LGPL-2.1-or-later
2+
// Copyright (c) 2012-2014 Monty Program Ab
3+
// Copyright (c) 2015-2021 MariaDB Corporation Ab
4+
5+
package org.mariadb.jdbc;
6+
7+
import java.sql.SQLException;
8+
import org.mariadb.jdbc.export.ExceptionFactory;
9+
10+
public class SimpleParameterMetaData implements java.sql.ParameterMetaData {
11+
12+
private final int paramCount;
13+
private final ExceptionFactory exceptionFactory;
14+
15+
protected SimpleParameterMetaData(ExceptionFactory exceptionFactory, int paramCount) {
16+
this.exceptionFactory = exceptionFactory;
17+
this.paramCount = paramCount;
18+
}
19+
20+
/**
21+
* Retrieves the number of parameters in the <code>PreparedStatement</code> object for which this
22+
* <code>ParameterMetaData</code> object contains information.
23+
*
24+
* @return the number of parameters
25+
*/
26+
@Override
27+
public int getParameterCount() {
28+
return paramCount;
29+
}
30+
31+
private void checkIndex(int index) throws SQLException {
32+
if (index < 1 || index > paramCount) {
33+
throw exceptionFactory.create(
34+
String.format(
35+
"Wrong index position. Is %s but must be in 1-%s range", index, paramCount));
36+
}
37+
}
38+
39+
/**
40+
* Retrieves whether null values are allowed in the designated parameter.
41+
*
42+
* @param idx the first parameter is 1, the second is 2, ...
43+
* @return the nullability status of the given parameter; one of <code>
44+
* ParameterMetaData.parameterNoNulls</code>, <code>ParameterMetaData.parameterNullable</code>
45+
* @throws SQLException if wrong index
46+
*/
47+
@Override
48+
public int isNullable(int idx) throws SQLException {
49+
checkIndex(idx);
50+
return java.sql.ParameterMetaData.parameterNullable;
51+
}
52+
53+
/**
54+
* Retrieves whether values for the designated parameter can be signed numbers.
55+
*
56+
* @param idx the first parameter is 1, the second is 2, ...
57+
* @return <code>true</code> if so; <code>false</code> otherwise
58+
* @throws SQLException if wrong index
59+
*/
60+
@Override
61+
public boolean isSigned(int idx) throws SQLException {
62+
checkIndex(idx);
63+
return true;
64+
}
65+
66+
/**
67+
* Retrieves the designated parameter's specified column size.
68+
*
69+
* <p>The returned value represents the maximum column size for the given parameter. For numeric
70+
* data, this is the maximum precision. For character data, this is the length in characters. For
71+
* datetime datatypes, this is the length in characters of the String representation (assuming the
72+
* maximum allowed precision of the fractional seconds component). For binary data, this is the
73+
* length in bytes. For the ROWID datatype, this is the length in bytes. 0 is returned for data
74+
* types where the column size is not applicable.
75+
*
76+
* @param idx the first parameter is 1, the second is 2, ...
77+
* @return precision
78+
* @throws SQLException if wrong index
79+
*/
80+
@Override
81+
public int getPrecision(int idx) throws SQLException {
82+
checkIndex(idx);
83+
throw exceptionFactory.create("Unknown parameter metadata precision");
84+
}
85+
86+
/**
87+
* Retrieves the designated parameter's number of digits to right of the decimal point. 0 is
88+
* returned for data types where the scale is not applicable. Parameter type are not sent by
89+
* server. See * https://jira.mariadb.org/browse/CONJ-568 and
90+
* https://jira.mariadb.org/browse/MDEV-15031
91+
*
92+
* @param idx the first parameter is 1, the second is 2, ...
93+
* @return scale
94+
* @throws SQLException if a database access error occurs
95+
*/
96+
@Override
97+
public int getScale(int idx) throws SQLException {
98+
checkIndex(idx);
99+
throw exceptionFactory.create("Unknown parameter metadata scale");
100+
}
101+
102+
/**
103+
* Retrieves the designated parameter's SQL type. Parameter type are not sent by server. See
104+
* https://jira.mariadb.org/browse/CONJ-568 and https://jira.mariadb.org/browse/MDEV-15031
105+
*
106+
* @param idx the first parameter is 1, the second is 2, ...
107+
* @return SQL types from <code>java.sql.Types</code>
108+
* @throws SQLException because not supported
109+
*/
110+
@Override
111+
public int getParameterType(int idx) throws SQLException {
112+
checkIndex(idx);
113+
throw exceptionFactory.create("Getting parameter type metadata is not supported", "0A000", -1);
114+
}
115+
116+
/**
117+
* Retrieves the designated parameter's database-specific type name.
118+
*
119+
* @param idx the first parameter is 1, the second is 2, ...
120+
* @return type the name used by the database. If the parameter type is a user-defined type, then
121+
* a fully-qualified type name is returned.
122+
* @throws SQLException if wrong index
123+
*/
124+
@Override
125+
public String getParameterTypeName(int idx) throws SQLException {
126+
checkIndex(idx);
127+
throw exceptionFactory.create("Unknown parameter metadata type name");
128+
}
129+
130+
/**
131+
* Retrieves the fully-qualified name of the Java class whose instances should be passed to the
132+
* method <code>PreparedStatement.setObject</code>.
133+
*
134+
* @param idx the first parameter is 1, the second is 2, ...
135+
* @return the fully-qualified name of the class in the Java programming language that would be
136+
* used by the method <code>PreparedStatement.setObject</code> to set the value in the
137+
* specified parameter. This is the class name used for custom mapping.
138+
* @throws SQLException if wrong index
139+
*/
140+
@Override
141+
public String getParameterClassName(int idx) throws SQLException {
142+
checkIndex(idx);
143+
throw exceptionFactory.create("Unknown parameter metadata class name", "0A000");
144+
}
145+
146+
/**
147+
* Retrieves the designated parameter's mode.
148+
*
149+
* @param idx the first parameter is 1, the second is 2, ...
150+
* @return mode of the parameter; one of <code>ParameterMetaData.parameterModeIn</code>, <code>
151+
* ParameterMetaData.parameterModeOut</code>, or <code>ParameterMetaData.parameterModeInOut
152+
* </code> <code>ParameterMetaData.parameterModeUnknown</code>.
153+
*/
154+
@Override
155+
public int getParameterMode(int idx) throws SQLException {
156+
checkIndex(idx);
157+
return java.sql.ParameterMetaData.parameterModeIn;
158+
}
159+
160+
/**
161+
* Returns an object that implements the given interface to allow access to non-standard methods,
162+
* or standard methods not exposed by the proxy.
163+
*
164+
* <p>If the receiver implements the interface then the result is the receiver or a proxy for the
165+
* receiver. If the receiver is a wrapper and the wrapped object implements the interface then the
166+
* result is the wrapped object or a proxy for the wrapped object. Otherwise, return the result of
167+
* calling <code>unwrap</code> recursively on the wrapped object or a proxy for that result. If
168+
* the receiver is not a wrapper and does not implement the interface, then an <code>SQLException
169+
* </code> is thrown.
170+
*
171+
* @param iface A Class defining an interface that the result must implement.
172+
* @return an object that implements the interface. Maybe a proxy for the actual implementing
173+
* object.
174+
* @throws SQLException If no object found that implements the interface
175+
*/
176+
@Override
177+
public <T> T unwrap(Class<T> iface) throws SQLException {
178+
if (isWrapperFor(iface)) {
179+
return iface.cast(this);
180+
}
181+
throw new SQLException("The receiver is not a wrapper for " + iface.getName());
182+
}
183+
184+
/**
185+
* Returns true if this either implements the interface argument or is directly or indirectly a
186+
* wrapper for an object that does. Returns false otherwise. If this implements the interface then
187+
* return true, else if this is a wrapper then return the result of recursively calling <code>
188+
* isWrapperFor</code> on the wrapped object. If this does not implement the interface and is not
189+
* a wrapper, return false. This method should be implemented as a low-cost operation compared to
190+
* <code>unwrap</code> so that callers can use this method to avoid expensive <code>unwrap</code>
191+
* calls that may fail. If this method returns true then calling <code>unwrap</code> with the same
192+
* argument should succeed.
193+
*
194+
* @param iface a Class defining an interface.
195+
* @return true if this implements the interface or directly or indirectly wraps an object that
196+
* does.
197+
*/
198+
@Override
199+
public boolean isWrapperFor(Class<?> iface) {
200+
return iface.isInstance(this);
201+
}
202+
}

src/test/java/org/mariadb/jdbc/integration/ParameterMetaDataTest.java

Lines changed: 65 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -60,25 +60,76 @@ private void parameterMetaDataTypeNotAvailable(org.mariadb.jdbc.Connection con)
6060
@Test
6161
public void parameterMetaDataNotPreparable() throws Exception {
6262
try (org.mariadb.jdbc.Connection con = createCon("&useServerPrepStmts=false")) {
63-
parameterMetaDataNotPreparable(con);
63+
// statement that cannot be prepared
64+
try (PreparedStatement pstmt =
65+
con.prepareStatement("select TMP.field1 from (select ? from dual) TMP")) {
66+
ParameterMetaData meta = pstmt.getParameterMetaData();
67+
assertEquals(1, meta.getParameterCount());
68+
assertEquals(ParameterMetaData.parameterModeIn, meta.getParameterMode(1));
69+
Common.assertThrowsContains(
70+
SQLSyntaxErrorException.class,
71+
() -> meta.getParameterTypeName(1),
72+
"Unknown parameter metadata type name");
73+
Common.assertThrowsContains(
74+
SQLFeatureNotSupportedException.class,
75+
() -> meta.getParameterClassName(1),
76+
"Unknown parameter metadata class name");
77+
Common.assertThrowsContains(
78+
SQLFeatureNotSupportedException.class,
79+
() -> meta.getParameterType(1),
80+
"Getting parameter type metadata is not supported");
81+
Common.assertThrowsContains(
82+
SQLSyntaxErrorException.class,
83+
() -> meta.getPrecision(1),
84+
"Unknown parameter metadata precision");
85+
Common.assertThrowsContains(
86+
SQLSyntaxErrorException.class,
87+
() -> meta.getScale(1),
88+
"Unknown parameter metadata scale");
89+
90+
Common.assertThrowsContains(
91+
SQLSyntaxErrorException.class,
92+
() -> meta.getParameterMode(0),
93+
"Wrong index position. Is 0 but must be in 1-1 range");
94+
Common.assertThrowsContains(
95+
SQLSyntaxErrorException.class,
96+
() -> meta.getParameterTypeName(0),
97+
"Wrong index position. Is 0 but must be in 1-1 range");
98+
Common.assertThrowsContains(
99+
SQLSyntaxErrorException.class,
100+
() -> meta.getParameterMode(0),
101+
"Wrong index position. Is 0 but must be in 1-1 range");
102+
Common.assertThrowsContains(
103+
SQLSyntaxErrorException.class,
104+
() -> meta.getParameterClassName(0),
105+
"Wrong index position. Is 0 but must be in 1-1 range");
106+
Common.assertThrowsContains(
107+
SQLSyntaxErrorException.class,
108+
() -> meta.getParameterType(2),
109+
"Wrong index position. Is 2 but must be in 1-1 range");
110+
Common.assertThrowsContains(
111+
SQLSyntaxErrorException.class,
112+
() -> meta.getPrecision(0),
113+
"Wrong index position. Is 0 but must be in 1-1 range");
114+
Common.assertThrowsContains(
115+
SQLSyntaxErrorException.class,
116+
() -> meta.getScale(0),
117+
"Wrong index position. Is 0 but must be in 1-1 range");
118+
}
64119
}
65120
try (org.mariadb.jdbc.Connection con = createCon("&useServerPrepStmts")) {
66-
parameterMetaDataNotPreparable(con);
67-
}
68-
}
69-
70-
private void parameterMetaDataNotPreparable(org.mariadb.jdbc.Connection con) throws SQLException {
71-
// statement that cannot be prepared
72-
try (PreparedStatement pstmt =
73-
con.prepareStatement("select TMP.field1 from (select ? from dual) TMP")) {
74-
try {
75-
pstmt.getParameterMetaData();
76-
fail();
121+
// statement that cannot be prepared
122+
try (PreparedStatement pstmt =
123+
con.prepareStatement("select TMP.field1 from (select ? from dual) TMP")) {
124+
try {
125+
pstmt.getParameterMetaData();
126+
fail();
127+
} catch (SQLSyntaxErrorException e) {
128+
// eat
129+
}
77130
} catch (SQLSyntaxErrorException e) {
78131
// eat
79132
}
80-
} catch (SQLSyntaxErrorException e) {
81-
// eat
82133
}
83134
}
84135

0 commit comments

Comments
 (0)