Mercurial > hg > monetdb-java
comparison src/main/java/org/monetdb/jdbc/MonetParameterMetaData.java @ 719:2f42195e9c58
Improved implementation of PreparedStatement.getParameterMetaData().
The previous implementation created a new ParameterMetaData object each time this method is called which is quite costly if it is called from inside a loop.
As the ParameterMetaData is static for a PreparedStatement it is better to create it once,
cache it in the PreparedStatement object and return the cached object for next calls to PreparedStatement.getParameterMetaData().
We also now create dedicated 1-based meta data arrays, such that we no longer have to call getParamIdx(param) in methods of ParameterMetaData.
author | Martin van Dinther <martin.van.dinther@monetdbsolutions.com> |
---|---|
date | Thu, 19 Jan 2023 16:46:44 +0100 (2023-01-19) |
parents | |
children | 99baab703566 |
comparison
equal
deleted
inserted
replaced
718:7138bbc6952e | 719:2f42195e9c58 |
---|---|
1 /* | |
2 * This Source Code Form is subject to the terms of the Mozilla Public | |
3 * License, v. 2.0. If a copy of the MPL was not distributed with this | |
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
5 * | |
6 * Copyright 1997 - July 2008 CWI, August 2008 - 2023 MonetDB B.V. | |
7 */ | |
8 | |
9 package org.monetdb.jdbc; | |
10 | |
11 import java.sql.ParameterMetaData; | |
12 import java.sql.SQLDataException; | |
13 import java.sql.SQLException; | |
14 import java.sql.Types; | |
15 | |
16 /** | |
17 *<pre> | |
18 * A {@link ParameterMetaData} suitable for the MonetDB database. | |
19 * | |
20 * An object that can be used to get information about the types and properties | |
21 * for each parameter marker in a PreparedStatement or CallableStatement object. | |
22 *</pre> | |
23 * | |
24 * @author Martin van Dinther | |
25 * @version 1.0 | |
26 */ | |
27 final class MonetParameterMetaData | |
28 extends MonetWrapper | |
29 implements ParameterMetaData | |
30 { | |
31 /** The parental Connection object */ | |
32 private final MonetConnection conn; | |
33 | |
34 /** The number of parameters, it can be zero !! */ | |
35 private final int paramCount; | |
36 | |
37 /** The MonetDB type names of the parameters in the PreparedStatement */ | |
38 private final String[] monetdbTypes; | |
39 /** The JDBC SQL type codes of the parameters in the PreparedStatement */ | |
40 private final int[] JdbcSQLTypes; | |
41 /** The precisions of the parameters in the PreparedStatement */ | |
42 private final int[] precisions; | |
43 /** The scales of the parameters in the PreparedStatement */ | |
44 private final int[] scales; | |
45 | |
46 /** | |
47 * Constructor backed by the given connection and metadata arrays. | |
48 * It is used by MonetPreparedStatement. | |
49 * | |
50 * @param connection the parent connection | |
51 * @param paramCount the number of parameters, it can be zero !! | |
52 * @param types the MonetDB type names | |
53 * @param jdbcTypes the JDBC SQL type codes | |
54 * @param precisions the precision for each parameter | |
55 * @param scales the scale for each parameter | |
56 * @throws IllegalArgumentException if called with null for one of the arguments | |
57 */ | |
58 MonetParameterMetaData( | |
59 final MonetConnection connection, | |
60 final int paramcount, | |
61 final String[] types, | |
62 final int[] jdbcTypes, | |
63 final int[] precisions, | |
64 final int[] scales) | |
65 throws IllegalArgumentException | |
66 { | |
67 if (connection == null) { | |
68 throw new IllegalArgumentException("Connection may not be null!"); | |
69 } | |
70 if (types == null) { | |
71 throw new IllegalArgumentException("MonetDB Types may not be null!"); | |
72 } | |
73 if (jdbcTypes == null) { | |
74 throw new IllegalArgumentException("JDBC Types may not be null!"); | |
75 } | |
76 if (precisions == null) { | |
77 throw new IllegalArgumentException("Precisions may not be null!"); | |
78 } | |
79 if (scales == null) { | |
80 throw new IllegalArgumentException("Scales may not be null!"); | |
81 } | |
82 if (types.length != precisions.length || types.length != (paramcount +1)) { | |
83 throw new IllegalArgumentException("Inconsistent Parameters metadata"); | |
84 } | |
85 this.conn = connection; | |
86 this.paramCount = paramcount; | |
87 this.monetdbTypes = types; | |
88 this.JdbcSQLTypes = jdbcTypes; | |
89 this.precisions = precisions; | |
90 this.scales = scales; | |
91 } | |
92 | |
93 /** | |
94 * Retrieves the number of parameters in the PreparedStatement object | |
95 * for which this ParameterMetaData object contains information. | |
96 * | |
97 * @return the number of parameters | |
98 */ | |
99 @Override | |
100 public int getParameterCount() { | |
101 return paramCount; | |
102 } | |
103 | |
104 /** | |
105 * Retrieves whether null values are allowed in the | |
106 * designated parameter. | |
107 * | |
108 * This is currently always unknown for MonetDB/SQL. | |
109 * | |
110 * @param param - the first parameter is 1, the second is 2, ... | |
111 * @return the nullability status of the given parameter; | |
112 * one of ParameterMetaData.parameterNoNulls, | |
113 * ParameterMetaData.parameterNullable, or | |
114 * ParameterMetaData.parameterNullableUnknown | |
115 */ | |
116 @Override | |
117 public int isNullable(final int param) throws SQLException { | |
118 checkParameterIndexValidity(param); | |
119 return ParameterMetaData.parameterNullableUnknown; | |
120 } | |
121 | |
122 /** | |
123 * Retrieves whether values for the designated parameter can | |
124 * be signed numbers. | |
125 * | |
126 * @param param - the first parameter is 1, the second is 2, ... | |
127 * @return true if so; false otherwise | |
128 * @throws SQLException if a database access error occurs | |
129 */ | |
130 @Override | |
131 public boolean isSigned(final int param) throws SQLException { | |
132 // we can hardcode this, based on the parameter type | |
133 switch (getParameterType(param)) { | |
134 case Types.TINYINT: | |
135 case Types.SMALLINT: | |
136 case Types.INTEGER: | |
137 case Types.REAL: | |
138 case Types.FLOAT: | |
139 case Types.DOUBLE: | |
140 case Types.DECIMAL: | |
141 case Types.NUMERIC: | |
142 return true; | |
143 case Types.BIGINT: | |
144 final String monettype = getParameterTypeName(param); | |
145 if (monettype != null && monettype.length() == 3) { | |
146 // data of type oid or ptr is not signed | |
147 if ("oid".equals(monettype) | |
148 || "ptr".equals(monettype)) | |
149 return false; | |
150 } | |
151 return true; | |
152 // All other types should return false | |
153 // case Types.BOOLEAN: | |
154 // case Types.DATE: // can year be negative? | |
155 // case Types.TIME: // can time be negative? | |
156 // case Types.TIME_WITH_TIMEZONE: | |
157 // case Types.TIMESTAMP: // can year be negative? | |
158 // case Types.TIMESTAMP_WITH_TIMEZONE: | |
159 default: | |
160 return false; | |
161 } | |
162 } | |
163 | |
164 /** | |
165 * Retrieves the designated parameter's specified column size. | |
166 * The returned value represents the maximum column size for | |
167 * the given parameter. | |
168 * For numeric data, this is the maximum precision. | |
169 * For character data, this is the length in characters. | |
170 * For datetime datatypes, this is the length in characters | |
171 * of the String representation (assuming the maximum allowed | |
172 * precision of the fractional seconds component). | |
173 * For binary data, this is the length in bytes. | |
174 * For the ROWID datatype, this is the length in bytes. | |
175 * 0 is returned for data types where the column size is not applicable. | |
176 * | |
177 * @param param - the first parameter is 1, the second is 2, ... | |
178 * @return precision | |
179 * @throws SQLException if a database access error occurs | |
180 */ | |
181 @Override | |
182 public int getPrecision(final int param) throws SQLException { | |
183 switch (getParameterType(param)) { | |
184 case Types.BIGINT: | |
185 return 19; | |
186 case Types.INTEGER: | |
187 return 10; | |
188 case Types.SMALLINT: | |
189 return 5; | |
190 case Types.TINYINT: | |
191 return 3; | |
192 case Types.REAL: | |
193 return 7; | |
194 case Types.FLOAT: | |
195 case Types.DOUBLE: | |
196 return 15; | |
197 case Types.DECIMAL: | |
198 case Types.NUMERIC: | |
199 // these data types have a variable precision (max precision is 38) | |
200 try { | |
201 return precisions[param]; | |
202 } catch (IndexOutOfBoundsException e) { | |
203 throw newSQLInvalidParameterIndexException(param); | |
204 } | |
205 case Types.CHAR: | |
206 case Types.VARCHAR: | |
207 case Types.LONGVARCHAR: // MonetDB doesn't use type LONGVARCHAR, it's here for completeness | |
208 case Types.CLOB: | |
209 // these data types have a variable length | |
210 try { | |
211 return precisions[param]; | |
212 } catch (IndexOutOfBoundsException e) { | |
213 throw newSQLInvalidParameterIndexException(param); | |
214 } | |
215 case Types.BINARY: | |
216 case Types.VARBINARY: | |
217 case Types.BLOB: | |
218 // these data types have a variable length | |
219 // It expect number of bytes, not number of hex chars | |
220 try { | |
221 return precisions[param]; | |
222 } catch (IndexOutOfBoundsException e) { | |
223 throw newSQLInvalidParameterIndexException(param); | |
224 } | |
225 case Types.DATE: | |
226 return 10; // 2020-10-08 | |
227 case Types.TIME: | |
228 return 15; // 21:51:34.399753 | |
229 case Types.TIME_WITH_TIMEZONE: | |
230 return 21; // 21:51:34.399753+02:00 | |
231 case Types.TIMESTAMP: | |
232 return 26; // 2020-10-08 21:51:34.399753 | |
233 case Types.TIMESTAMP_WITH_TIMEZONE: | |
234 return 32; // 2020-10-08 21:51:34.399753+02:00 | |
235 case Types.BOOLEAN: | |
236 return 1; | |
237 default: | |
238 // All other types should return 0 | |
239 return 0; | |
240 } | |
241 } | |
242 | |
243 /** | |
244 * Retrieves the designated parameter's number of digits to | |
245 * right of the decimal point. | |
246 * 0 is returned for data types where the scale is not applicable. | |
247 * | |
248 * @param param - the first parameter is 1, the second is 2, ... | |
249 * @return scale | |
250 * @throws SQLException if a database access error occurs | |
251 */ | |
252 @Override | |
253 public int getScale(final int param) throws SQLException { | |
254 checkParameterIndexValidity(param); | |
255 try { | |
256 return scales[param]; | |
257 } catch (IndexOutOfBoundsException e) { | |
258 throw newSQLInvalidParameterIndexException(param); | |
259 } | |
260 } | |
261 | |
262 /** | |
263 * Retrieves the designated parameter's SQL type. | |
264 * | |
265 * @param param - the first parameter is 1, the second is 2, ... | |
266 * @return SQL type from java.sql.Types | |
267 * @throws SQLException if a database access error occurs | |
268 */ | |
269 @Override | |
270 public int getParameterType(final int param) throws SQLException { | |
271 checkParameterIndexValidity(param); | |
272 try { | |
273 return JdbcSQLTypes[param]; | |
274 } catch (IndexOutOfBoundsException e) { | |
275 throw newSQLInvalidParameterIndexException(param); | |
276 } | |
277 } | |
278 | |
279 /** | |
280 * Retrieves the designated parameter's database-specific type name. | |
281 * | |
282 * @param param - the first parameter is 1, the second is 2, ... | |
283 * @return type the name used by the database. If the | |
284 * parameter type is a user-defined type, then a | |
285 * fully-qualified type name is returned. | |
286 * @throws SQLException if a database access error occurs | |
287 */ | |
288 @Override | |
289 public String getParameterTypeName(final int param) throws SQLException { | |
290 checkParameterIndexValidity(param); | |
291 try { | |
292 final String monettype = monetdbTypes[param]; | |
293 if (monettype.endsWith("_interval")) { | |
294 /* convert the interval type names to valid SQL data type names */ | |
295 if ("day_interval".equals(monettype)) | |
296 return "interval day"; | |
297 if ("month_interval".equals(monettype)) | |
298 return "interval month"; | |
299 if ("sec_interval".equals(monettype)) | |
300 return "interval second"; | |
301 } | |
302 return monettype; | |
303 } catch (IndexOutOfBoundsException e) { | |
304 throw newSQLInvalidParameterIndexException(param); | |
305 } | |
306 } | |
307 | |
308 /** | |
309 * Retrieves the fully-qualified name of the Java class whose instances | |
310 * should be passed to the method PreparedStatement.setObject. | |
311 * | |
312 * @param param - the first parameter is 1, the second is 2, ... | |
313 * @return the fully-qualified name of the class in the Java | |
314 * programming language that would be used by the | |
315 * method PreparedStatement.setObject to set the | |
316 * value in the specified parameter. This is the | |
317 * class name used for custom mapping. | |
318 * @throws SQLException if a database access error occurs | |
319 */ | |
320 @Override | |
321 public String getParameterClassName(final int param) throws SQLException { | |
322 final String MonetDBType = getParameterTypeName(param); | |
323 final java.util.Map<String,Class<?>> map = conn.getTypeMap(); | |
324 final Class<?> type; | |
325 if (map != null && map.containsKey(MonetDBType)) { | |
326 type = (Class)map.get(MonetDBType); | |
327 } else { | |
328 type = MonetDriver.getClassForType(getParameterType(param)); | |
329 } | |
330 if (type != null) { | |
331 return type.getCanonicalName(); | |
332 } | |
333 throw new SQLException("parameter type mapping null: " + MonetDBType, "M0M03"); | |
334 } | |
335 | |
336 /** | |
337 * Retrieves the designated parameter's mode. | |
338 * For MonetDB/SQL we currently only support INput parameters. | |
339 * | |
340 * @param param - the first parameter is 1, the second is 2, ... | |
341 * @return mode of the parameter; one of | |
342 * ParameterMetaData.parameterModeIn, | |
343 * ParameterMetaData.parameterModeOut, | |
344 * ParameterMetaData.parameterModeInOut or | |
345 * ParameterMetaData.parameterModeUnknown. | |
346 */ | |
347 @Override | |
348 public int getParameterMode(final int param) throws SQLException { | |
349 checkParameterIndexValidity(param); | |
350 return ParameterMetaData.parameterModeIn; | |
351 } | |
352 | |
353 | |
354 /** | |
355 * A private utility method to check validity of parameter index number | |
356 * | |
357 * @param param - the first parameter is 1, the second is 2, ... | |
358 * @throws SQLDataException when invalid parameter index number | |
359 */ | |
360 private final void checkParameterIndexValidity(final int param) throws SQLDataException { | |
361 if (param < 1 || param > paramCount) | |
362 throw newSQLInvalidParameterIndexException(param); | |
363 } | |
364 | |
365 /** | |
366 * Small helper method that formats the "Invalid Parameter Index number ..." | |
367 * message and creates a new SQLDataException object whose SQLState is set | |
368 * to "22010": invalid indicator parameter value. | |
369 * | |
370 * @param paramIdx the parameter index number | |
371 * @return a new created SQLDataException object with SQLState 22010 | |
372 */ | |
373 private final SQLDataException newSQLInvalidParameterIndexException(final int paramIdx) { | |
374 return new SQLDataException("Invalid Parameter Index number: " + paramIdx, "22010"); | |
375 } | |
376 } |