1 | /* |
2 | * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, |
3 | * and the EPL 1.0 (http://h2database.com/html/license.html). |
4 | * Initial Developer: H2 Group |
5 | */ |
6 | package org.h2.jdbc; |
7 | |
8 | import java.sql.ResultSetMetaData; |
9 | import java.sql.SQLException; |
10 | import org.h2.message.DbException; |
11 | import org.h2.message.Trace; |
12 | import org.h2.message.TraceObject; |
13 | import org.h2.result.ResultInterface; |
14 | import org.h2.util.MathUtils; |
15 | import org.h2.value.DataType; |
16 | |
17 | /** |
18 | * Represents the meta data for a ResultSet. |
19 | */ |
20 | public class JdbcResultSetMetaData extends TraceObject implements |
21 | ResultSetMetaData { |
22 | |
23 | private final String catalog; |
24 | private final JdbcResultSet rs; |
25 | private final JdbcPreparedStatement prep; |
26 | private final ResultInterface result; |
27 | private final int columnCount; |
28 | |
29 | JdbcResultSetMetaData(JdbcResultSet rs, JdbcPreparedStatement prep, |
30 | ResultInterface result, String catalog, Trace trace, int id) { |
31 | setTrace(trace, TraceObject.RESULT_SET_META_DATA, id); |
32 | this.catalog = catalog; |
33 | this.rs = rs; |
34 | this.prep = prep; |
35 | this.result = result; |
36 | this.columnCount = result.getVisibleColumnCount(); |
37 | } |
38 | |
39 | /** |
40 | * Returns the number of columns. |
41 | * |
42 | * @return the number of columns |
43 | * @throws SQLException if the result set is closed or invalid |
44 | */ |
45 | @Override |
46 | public int getColumnCount() throws SQLException { |
47 | try { |
48 | debugCodeCall("getColumnCount"); |
49 | checkClosed(); |
50 | return columnCount; |
51 | } catch (Exception e) { |
52 | throw logAndConvert(e); |
53 | } |
54 | } |
55 | |
56 | /** |
57 | * Returns the column label. |
58 | * |
59 | * @param column the column index (1,2,...) |
60 | * @return the column label |
61 | * @throws SQLException if the result set is closed or invalid |
62 | */ |
63 | @Override |
64 | public String getColumnLabel(int column) throws SQLException { |
65 | try { |
66 | debugCodeCall("getColumnLabel", column); |
67 | checkColumnIndex(column); |
68 | return result.getAlias(--column); |
69 | } catch (Exception e) { |
70 | throw logAndConvert(e); |
71 | } |
72 | } |
73 | |
74 | /** |
75 | * Returns the column name. |
76 | * |
77 | * @param column the column index (1,2,...) |
78 | * @return the column name |
79 | * @throws SQLException if the result set is closed or invalid |
80 | */ |
81 | @Override |
82 | public String getColumnName(int column) throws SQLException { |
83 | try { |
84 | debugCodeCall("getColumnName", column); |
85 | checkColumnIndex(column); |
86 | return result.getColumnName(--column); |
87 | } catch (Exception e) { |
88 | throw logAndConvert(e); |
89 | } |
90 | } |
91 | |
92 | /** |
93 | * Returns the data type of a column. |
94 | * See also java.sql.Type. |
95 | * |
96 | * @param column the column index (1,2,...) |
97 | * @return the data type |
98 | * @throws SQLException if the result set is closed or invalid |
99 | */ |
100 | @Override |
101 | public int getColumnType(int column) throws SQLException { |
102 | try { |
103 | debugCodeCall("getColumnType", column); |
104 | checkColumnIndex(column); |
105 | int type = result.getColumnType(--column); |
106 | return DataType.convertTypeToSQLType(type); |
107 | } catch (Exception e) { |
108 | throw logAndConvert(e); |
109 | } |
110 | } |
111 | |
112 | /** |
113 | * Returns the data type name of a column. |
114 | * |
115 | * @param column the column index (1,2,...) |
116 | * @return the data type name |
117 | * @throws SQLException if the result set is closed or invalid |
118 | */ |
119 | @Override |
120 | public String getColumnTypeName(int column) throws SQLException { |
121 | try { |
122 | debugCodeCall("getColumnTypeName", column); |
123 | checkColumnIndex(column); |
124 | int type = result.getColumnType(--column); |
125 | return DataType.getDataType(type).name; |
126 | } catch (Exception e) { |
127 | throw logAndConvert(e); |
128 | } |
129 | } |
130 | |
131 | /** |
132 | * Returns the schema name. |
133 | * |
134 | * @param column the column index (1,2,...) |
135 | * @return the schema name, or "" (an empty string) if not applicable |
136 | * @throws SQLException if the result set is closed or invalid |
137 | */ |
138 | @Override |
139 | public String getSchemaName(int column) throws SQLException { |
140 | try { |
141 | debugCodeCall("getSchemaName", column); |
142 | checkColumnIndex(column); |
143 | String schema = result.getSchemaName(--column); |
144 | return schema == null ? "" : schema; |
145 | } catch (Exception e) { |
146 | throw logAndConvert(e); |
147 | } |
148 | } |
149 | |
150 | /** |
151 | * Returns the table name. |
152 | * |
153 | * @param column the column index (1,2,...) |
154 | * @return the table name |
155 | * @throws SQLException if the result set is closed or invalid |
156 | */ |
157 | @Override |
158 | public String getTableName(int column) throws SQLException { |
159 | try { |
160 | debugCodeCall("getTableName", column); |
161 | checkColumnIndex(column); |
162 | String table = result.getTableName(--column); |
163 | return table == null ? "" : table; |
164 | } catch (Exception e) { |
165 | throw logAndConvert(e); |
166 | } |
167 | } |
168 | |
169 | /** |
170 | * Returns the catalog name. |
171 | * |
172 | * @param column the column index (1,2,...) |
173 | * @return the catalog name |
174 | * @throws SQLException if the result set is closed or invalid |
175 | */ |
176 | @Override |
177 | public String getCatalogName(int column) throws SQLException { |
178 | try { |
179 | debugCodeCall("getCatalogName", column); |
180 | checkColumnIndex(column); |
181 | return catalog == null ? "" : catalog; |
182 | } catch (Exception e) { |
183 | throw logAndConvert(e); |
184 | } |
185 | } |
186 | |
187 | /** |
188 | * Checks if this an autoincrement column. |
189 | * |
190 | * @param column the column index (1,2,...) |
191 | * @return false |
192 | * @throws SQLException if the result set is closed or invalid |
193 | */ |
194 | @Override |
195 | public boolean isAutoIncrement(int column) throws SQLException { |
196 | try { |
197 | debugCodeCall("isAutoIncrement", column); |
198 | checkColumnIndex(column); |
199 | return result.isAutoIncrement(--column); |
200 | } catch (Exception e) { |
201 | throw logAndConvert(e); |
202 | } |
203 | } |
204 | |
205 | /** |
206 | * Checks if this column is case sensitive. |
207 | * It always returns true. |
208 | * |
209 | * @param column the column index (1,2,...) |
210 | * @return true |
211 | * @throws SQLException if the result set is closed or invalid |
212 | */ |
213 | @Override |
214 | public boolean isCaseSensitive(int column) throws SQLException { |
215 | try { |
216 | debugCodeCall("isCaseSensitive", column); |
217 | checkColumnIndex(column); |
218 | return true; |
219 | } catch (Exception e) { |
220 | throw logAndConvert(e); |
221 | } |
222 | } |
223 | |
224 | /** |
225 | * Checks if this column is searchable. |
226 | * It always returns true. |
227 | * |
228 | * @param column the column index (1,2,...) |
229 | * @return true |
230 | * @throws SQLException if the result set is closed or invalid |
231 | */ |
232 | @Override |
233 | public boolean isSearchable(int column) throws SQLException { |
234 | try { |
235 | debugCodeCall("isSearchable", column); |
236 | checkColumnIndex(column); |
237 | return true; |
238 | } catch (Exception e) { |
239 | throw logAndConvert(e); |
240 | } |
241 | } |
242 | |
243 | /** |
244 | * Checks if this is a currency column. |
245 | * It always returns false. |
246 | * |
247 | * @param column the column index (1,2,...) |
248 | * @return false |
249 | * @throws SQLException if the result set is closed or invalid |
250 | */ |
251 | @Override |
252 | public boolean isCurrency(int column) throws SQLException { |
253 | try { |
254 | debugCodeCall("isCurrency", column); |
255 | checkColumnIndex(column); |
256 | return false; |
257 | } catch (Exception e) { |
258 | throw logAndConvert(e); |
259 | } |
260 | } |
261 | |
262 | /** |
263 | * Checks if this is nullable column. Returns |
264 | * ResultSetMetaData.columnNullableUnknown if this is not a column of a |
265 | * table. Otherwise, it returns ResultSetMetaData.columnNoNulls if the |
266 | * column is not nullable, and ResultSetMetaData.columnNullable if it is |
267 | * nullable. |
268 | * |
269 | * @param column the column index (1,2,...) |
270 | * @return ResultSetMetaData.column* |
271 | * @throws SQLException if the result set is closed or invalid |
272 | */ |
273 | @Override |
274 | public int isNullable(int column) throws SQLException { |
275 | try { |
276 | debugCodeCall("isNullable", column); |
277 | checkColumnIndex(column); |
278 | return result.getNullable(--column); |
279 | } catch (Exception e) { |
280 | throw logAndConvert(e); |
281 | } |
282 | } |
283 | |
284 | /** |
285 | * Checks if this column is signed. |
286 | * It always returns true. |
287 | * |
288 | * @param column the column index (1,2,...) |
289 | * @return true |
290 | * @throws SQLException if the result set is closed or invalid |
291 | */ |
292 | @Override |
293 | public boolean isSigned(int column) throws SQLException { |
294 | try { |
295 | debugCodeCall("isSigned", column); |
296 | checkColumnIndex(column); |
297 | return true; |
298 | } catch (Exception e) { |
299 | throw logAndConvert(e); |
300 | } |
301 | } |
302 | |
303 | /** |
304 | * Checks if this column is read only. |
305 | * It always returns false. |
306 | * |
307 | * @param column the column index (1,2,...) |
308 | * @return false |
309 | * @throws SQLException if the result set is closed or invalid |
310 | */ |
311 | @Override |
312 | public boolean isReadOnly(int column) throws SQLException { |
313 | try { |
314 | debugCodeCall("isReadOnly", column); |
315 | checkColumnIndex(column); |
316 | return false; |
317 | } catch (Exception e) { |
318 | throw logAndConvert(e); |
319 | } |
320 | } |
321 | |
322 | /** |
323 | * Checks whether it is possible for a write on this column to succeed. |
324 | * It always returns true. |
325 | * |
326 | * @param column the column index (1,2,...) |
327 | * @return true |
328 | * @throws SQLException if the result set is closed or invalid |
329 | */ |
330 | @Override |
331 | public boolean isWritable(int column) throws SQLException { |
332 | try { |
333 | debugCodeCall("isWritable", column); |
334 | checkColumnIndex(column); |
335 | return true; |
336 | } catch (Exception e) { |
337 | throw logAndConvert(e); |
338 | } |
339 | } |
340 | |
341 | /** |
342 | * Checks whether a write on this column will definitely succeed. |
343 | * It always returns false. |
344 | * |
345 | * @param column the column index (1,2,...) |
346 | * @return false |
347 | * @throws SQLException if the result set is closed or invalid |
348 | */ |
349 | @Override |
350 | public boolean isDefinitelyWritable(int column) throws SQLException { |
351 | try { |
352 | debugCodeCall("isDefinitelyWritable", column); |
353 | checkColumnIndex(column); |
354 | return false; |
355 | } catch (Exception e) { |
356 | throw logAndConvert(e); |
357 | } |
358 | } |
359 | |
360 | /** |
361 | * Gets the Java class name of the object that will be returned |
362 | * if ResultSet.getObject is called. |
363 | * |
364 | * @param column the column index (1,2,...) |
365 | * @return the Java class name |
366 | * @throws SQLException if the result set is closed or invalid |
367 | */ |
368 | @Override |
369 | public String getColumnClassName(int column) throws SQLException { |
370 | try { |
371 | debugCodeCall("getColumnClassName", column); |
372 | checkColumnIndex(column); |
373 | int type = result.getColumnType(--column); |
374 | return DataType.getTypeClassName(type); |
375 | } catch (Exception e) { |
376 | throw logAndConvert(e); |
377 | } |
378 | } |
379 | |
380 | /** |
381 | * Gets the precision for this column. |
382 | * |
383 | * @param column the column index (1,2,...) |
384 | * @return the precision |
385 | * @throws SQLException if the result set is closed or invalid |
386 | */ |
387 | @Override |
388 | public int getPrecision(int column) throws SQLException { |
389 | try { |
390 | debugCodeCall("getPrecision", column); |
391 | checkColumnIndex(column); |
392 | long prec = result.getColumnPrecision(--column); |
393 | return MathUtils.convertLongToInt(prec); |
394 | } catch (Exception e) { |
395 | throw logAndConvert(e); |
396 | } |
397 | } |
398 | |
399 | /** |
400 | * Gets the scale for this column. |
401 | * |
402 | * @param column the column index (1,2,...) |
403 | * @return the scale |
404 | * @throws SQLException if the result set is closed or invalid |
405 | */ |
406 | @Override |
407 | public int getScale(int column) throws SQLException { |
408 | try { |
409 | debugCodeCall("getScale", column); |
410 | checkColumnIndex(column); |
411 | return result.getColumnScale(--column); |
412 | } catch (Exception e) { |
413 | throw logAndConvert(e); |
414 | } |
415 | } |
416 | |
417 | /** |
418 | * Gets the maximum display size for this column. |
419 | * |
420 | * @param column the column index (1,2,...) |
421 | * @return the display size |
422 | * @throws SQLException if the result set is closed or invalid |
423 | */ |
424 | @Override |
425 | public int getColumnDisplaySize(int column) throws SQLException { |
426 | try { |
427 | debugCodeCall("getColumnDisplaySize", column); |
428 | checkColumnIndex(column); |
429 | return result.getDisplaySize(--column); |
430 | } catch (Exception e) { |
431 | throw logAndConvert(e); |
432 | } |
433 | } |
434 | |
435 | private void checkClosed() { |
436 | if (rs != null) { |
437 | rs.checkClosed(); |
438 | } |
439 | if (prep != null) { |
440 | prep.checkClosed(); |
441 | } |
442 | } |
443 | |
444 | private void checkColumnIndex(int columnIndex) { |
445 | checkClosed(); |
446 | if (columnIndex < 1 || columnIndex > columnCount) { |
447 | throw DbException.getInvalidValueException("columnIndex", columnIndex); |
448 | } |
449 | } |
450 | |
451 | /** |
452 | * Return an object of this class if possible. |
453 | * |
454 | * @param iface the class |
455 | * @return this |
456 | */ |
457 | @Override |
458 | @SuppressWarnings("unchecked") |
459 | public <T> T unwrap(Class<T> iface) throws SQLException { |
460 | if (isWrapperFor(iface)) { |
461 | return (T) this; |
462 | } |
463 | throw DbException.getInvalidValueException("iface", iface); |
464 | } |
465 | |
466 | /** |
467 | * Checks if unwrap can return an object of this class. |
468 | * |
469 | * @param iface the class |
470 | * @return whether or not the interface is assignable from this class |
471 | */ |
472 | @Override |
473 | public boolean isWrapperFor(Class<?> iface) throws SQLException { |
474 | return iface != null && iface.isAssignableFrom(getClass()); |
475 | } |
476 | |
477 | /** |
478 | * INTERNAL |
479 | */ |
480 | @Override |
481 | public String toString() { |
482 | return getTraceObjectName() + ": columns=" + columnCount; |
483 | } |
484 | |
485 | } |