Source code: com/mysql/jdbc/ResultSetMetaData.java
1 /*
2 Copyright (C) 2002-2004 MySQL AB
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of version 2 of the GNU General Public License as
6 published by the Free Software Foundation.
7
8
9 There are special exceptions to the terms and conditions of the GPL
10 as it is applied to this software. View the full text of the
11 exception exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
12 software distribution.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
23 */
24 package com.mysql.jdbc;
25
26 import java.sql.SQLException;
27 import java.sql.Types;
28
29
30 /**
31 * A ResultSetMetaData object can be used to find out about the types and
32 * properties of the columns in a ResultSet
33 *
34 * @see java.sql.ResultSetMetaData
35 * @author Mark Matthews
36 * @version $Id: ResultSetMetaData.java,v 1.12.2.12 2004/09/13 18:03:37 mmatthew Exp $
37 */
38 public class ResultSetMetaData implements java.sql.ResultSetMetaData {
39 Field[] fields;
40
41 /**
42 * Initialise for a result with a tuple set and
43 * a field descriptor set
44 *
45 * @param fields the array of field descriptors
46
47 */
48 public ResultSetMetaData(Field[] fields) {
49 this.fields = fields;
50 }
51
52 /**
53 * Is the column automatically numbered (and thus read-only)
54 *
55 * MySQL Auto-increment columns are not read only,
56 * so to conform to the spec, this method returns false.
57 *
58 * @param column the first column is 1, the second is 2...
59 * @return true if so
60 * @throws java.sql.SQLException if a database access error occurs
61 */
62 public boolean isAutoIncrement(int column) throws java.sql.SQLException {
63 Field f = getField(column);
64
65 return f.isAutoIncrement();
66 }
67
68 /**
69 * Does a column's case matter? ASSUMPTION: Any field that is
70 * not obviously case insensitive is assumed to be case sensitive
71 *
72 * @param column the first column is 1, the second is 2...
73 * @return true if so
74 * @throws java.sql.SQLException if a database access error occurs
75 */
76 public boolean isCaseSensitive(int column) throws java.sql.SQLException {
77 Field field = getField(column);
78
79 int sqlType = field.getSQLType();
80
81 switch (sqlType) {
82 case Types.BIT:
83 case Types.TINYINT:
84 case Types.SMALLINT:
85 case Types.INTEGER:
86 case Types.BIGINT:
87 case Types.FLOAT:
88 case Types.REAL:
89 case Types.DOUBLE:
90 case Types.DATE:
91 case Types.TIME:
92 case Types.TIMESTAMP:
93 return false;
94
95 case Types.CHAR:
96 case Types.VARCHAR:
97
98 return field.isBinary();
99
100 default:
101 return true;
102 }
103 }
104
105 /**
106 * What's a column's table's catalog name?
107 *
108 * @param column the first column is 1, the second is 2...
109 * @return catalog name, or "" if not applicable
110 * @throws java.sql.SQLException if a database access error occurs
111 */
112 public String getCatalogName(int column) throws java.sql.SQLException {
113 Field f = getField(column);
114
115 String database = f.getDatabaseName();
116
117 return (database == null) ? "" : database;
118 }
119
120 //--------------------------JDBC 2.0-----------------------------------
121
122 /**
123 * JDBC 2.0
124 *
125 * <p>Return the fully qualified name of the Java class whose instances
126 * are manufactured if ResultSet.getObject() is called to retrieve a value
127 * from the column. ResultSet.getObject() may return a subClass of the
128 * class returned by this method.
129 *
130 * @param column the column number to retrieve information for
131 * @return the fully qualified name of the Java class whose instances
132 * are manufactured if ResultSet.getObject() is called to retrieve a value
133 * from the column.
134 *
135 * @throws SQLException if an error occurs
136 */
137 public String getColumnClassName(int column) throws SQLException {
138 Field f = getField(column);
139
140 // From JDBC-3.0 spec
141 //
142 // JDBC Type Java Object Type
143 //
144 // CHAR String
145 // VARCHAR String
146 // LONGVARCHAR String
147 // NUMERIC java.math.BigDecimal
148 // DECIMAL java.math.BigDecimal
149 // BIT Boolean
150 // BOOLEAN Boolean
151 // TINYINT Integer
152 // SMALLINT Integer
153 // INTEGER Integer
154 // BIGINT Long
155 // REAL Float
156 // FLOAT Double
157 // DOUBLE Double
158 // BINARY byte[]
159 // VARBINARY byte[]
160 // LONGVARBINARY byte[]
161 // DATE java.sql.Date
162 // TIME java.sql.Time
163 // TIMESTAMP java.sql.Timestamp
164 // DISTINCT Object type of underlying type
165 // CLOB Clob
166 // BLOB Blob
167 // ARRAY Array
168 // STRUCT Struct or SQLData
169 // REF Ref
170 // DATALINK java.net.URL
171 // JAVA_OBJECT underlying Java class
172
173 switch (f.getSQLType()) {
174 case Types.BIT:
175 case Types.BOOLEAN:
176 return "java.lang.Boolean";
177
178 case Types.TINYINT:
179
180 return "java.lang.Integer";
181
182 case Types.SMALLINT:
183
184 return "java.lang.Integer";
185
186 case Types.INTEGER:
187
188 if (f.isUnsigned()) {
189 return "java.lang.Long";
190 } else {
191 return "java.lang.Integer";
192 }
193
194 case Types.BIGINT:
195
196 return "java.lang.Long";
197
198 case Types.DECIMAL:
199 case Types.NUMERIC:
200
201 return "java.math.BigDecimal";
202
203 case Types.REAL:
204
205 return "java.lang.Float";
206
207 case Types.FLOAT:
208 case Types.DOUBLE:
209
210 return "java.lang.Double";
211
212 case Types.CHAR:
213 case Types.VARCHAR:
214 case Types.LONGVARCHAR:
215
216 return "java.lang.String";
217
218 case Types.BINARY:
219 case Types.VARBINARY:
220 case Types.LONGVARBINARY:
221
222 if (!f.isBlob()) {
223 return "java.lang.String";
224 } else if (!f.isBinary()) {
225 return "java.lang.String";
226 } else {
227 return "[B";
228 }
229
230 case Types.DATE:
231
232 return "java.sql.Date";
233
234 case Types.TIME:
235
236 return "java.sql.Time";
237
238 case Types.TIMESTAMP:
239
240 return "java.sql.Timestamp";
241
242 default:
243
244 return "java.lang.Object";
245 }
246 }
247
248 /**
249 * What's the MySQL character set name for the given column?
250 *
251 * @param column the first column is 1, the second is 2, etc.
252 *
253 * @return the MySQL character set name for the given column
254 * @throws SQLException if an invalid column index is given.
255 */
256 public String getColumnCharacterSet(int column) throws SQLException {
257 return getField(column).getCharacterSet();
258 }
259
260 /**
261 * What's the Java character encoding name for the given column?
262 *
263 * @param column the first column is 1, the second is 2, etc.
264 *
265 * @return the Java character encoding name for the given column,
266 * or null if no Java character encoding maps to the MySQL character set
267 * for the given column.
268 *
269 * @throws SQLException if an invalid column index is given.
270 */
271 public String getColumnCharacterEncoding(int column) throws SQLException {
272 String mysqlName = getColumnCharacterSet(column);
273
274 String javaName = null;
275
276 if (mysqlName != null) {
277 javaName = (String)CharsetMapping.MYSQL_TO_JAVA_CHARSET_MAP.get(mysqlName);
278 }
279
280 return javaName;
281 }
282
283 /**
284 * Whats the number of columns in the ResultSet?
285 *
286 * @return the number
287 * @throws java.sql.SQLException if a database access error occurs
288 */
289 public int getColumnCount() throws java.sql.SQLException {
290 return fields.length;
291 }
292
293 /**
294 * What is the column's normal maximum width in characters?
295 *
296 * @param column the first column is 1, the second is 2, etc.
297 * @return the maximum width
298 * @throws java.sql.SQLException if a database access error occurs
299 */
300 public int getColumnDisplaySize(int column) throws java.sql.SQLException {
301 return (int)getField(column).getLength();
302 }
303
304 /**
305 * What is the suggested column title for use in printouts and
306 * displays?
307 *
308 * @param column the first column is 1, the second is 2, etc.
309 * @return the column label
310 * @throws java.sql.SQLException if a database access error occurs
311 */
312 public String getColumnLabel(int column) throws java.sql.SQLException {
313 return getColumnName(column);
314 }
315
316 /**
317 * What's a column's name?
318 *
319 * @param column the first column is 1, the second is 2, etc.
320 * @return the column name
321 * @throws java.sql.SQLException if a databvase access error occurs
322 */
323 public String getColumnName(int column) throws java.sql.SQLException {
324 return getField(column).getName();
325 }
326
327 /**
328 * What is a column's SQL Type? (java.sql.Type int)
329 *
330 * @param column the first column is 1, the second is 2, etc.
331 * @return the java.sql.Type value
332 * @throws java.sql.SQLException if a database access error occurs
333 * @see java.sql.Types
334 */
335 public int getColumnType(int column) throws java.sql.SQLException {
336 return getField(column).getSQLType();
337 }
338
339 /**
340 * Whats is the column's data source specific type name?
341 *
342 * @param column the first column is 1, the second is 2, etc.
343 * @return the type name
344 * @throws java.sql.SQLException if a database access error occurs
345 */
346 public String getColumnTypeName(int column) throws java.sql.SQLException {
347 Field field = getField(column);
348
349 int mysqlType = field.getMysqlType();
350
351 switch (mysqlType) {
352 case MysqlDefs.FIELD_TYPE_DECIMAL:
353 return field.isUnsigned() ? "DECIMAL UNSIGNED" : "DECIMAL";
354
355 case MysqlDefs.FIELD_TYPE_TINY:
356 return field.isUnsigned() ? "TINYINT UNSIGNED" : "TINYINT";
357
358 case MysqlDefs.FIELD_TYPE_SHORT:
359 return field.isUnsigned() ? "SMALLINT UNSIGNED" : "SMALLINT";
360
361 case MysqlDefs.FIELD_TYPE_LONG:
362 return field.isUnsigned() ? "INTEGER UNSIGNED" : "INTEGER";
363
364 case MysqlDefs.FIELD_TYPE_FLOAT:
365 return field.isUnsigned() ? "FLOAT UNSIGNED" : "FLOAT";
366
367 case MysqlDefs.FIELD_TYPE_DOUBLE:
368 return field.isUnsigned() ? "DOUBLE UNSIGNED" : "DOUBLE";
369
370 case MysqlDefs.FIELD_TYPE_NULL:
371 return "NULL";
372
373 case MysqlDefs.FIELD_TYPE_TIMESTAMP:
374 return "TIMESTAMP";
375
376 case MysqlDefs.FIELD_TYPE_LONGLONG:
377 return field.isUnsigned() ? "BIGINT UNSIGNED" : "BIGINT";
378
379 case MysqlDefs.FIELD_TYPE_INT24:
380 return field.isUnsigned() ? "MEDIUMINT UNSIGNED" : "MEDIUMINT";
381
382 case MysqlDefs.FIELD_TYPE_DATE:
383 return "DATE";
384
385 case MysqlDefs.FIELD_TYPE_TIME:
386 return "TIME";
387
388 case MysqlDefs.FIELD_TYPE_DATETIME:
389 return "DATETIME";
390
391 case MysqlDefs.FIELD_TYPE_TINY_BLOB:
392 return "TINYBLOB";
393
394 case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB:
395 return "MEDIUMBLOB";
396
397 case MysqlDefs.FIELD_TYPE_LONG_BLOB:
398 return "LONGBLOB";
399
400 case MysqlDefs.FIELD_TYPE_BLOB:
401
402 if (getField(column).isBinary()) {
403 return "BLOB";
404 } else {
405 return "TEXT";
406 }
407
408 case MysqlDefs.FIELD_TYPE_VAR_STRING:
409 return "VARCHAR";
410
411 case MysqlDefs.FIELD_TYPE_STRING:
412 return "CHAR";
413
414 case MysqlDefs.FIELD_TYPE_ENUM:
415 return "ENUM";
416
417 case MysqlDefs.FIELD_TYPE_SET:
418 return "SET";
419
420 case MysqlDefs.FIELD_TYPE_YEAR:
421 return "YEAR";
422
423 default:
424 return "UNKNOWN";
425 }
426 }
427
428 /**
429 * Is the column a cash value?
430 *
431 * @param column the first column is 1, the second is 2...
432 * @return true if its a cash column
433 * @throws java.sql.SQLException if a database access error occurs
434 */
435 public boolean isCurrency(int column) throws java.sql.SQLException {
436 return false;
437 }
438
439 /**
440 * Will a write on this column definately succeed?
441 *
442 * @param column the first column is 1, the second is 2, etc..
443 * @return true if so
444 * @throws java.sql.SQLException if a database access error occurs
445 */
446 public boolean isDefinitelyWritable(int column)
447 throws java.sql.SQLException {
448 return isWritable(column);
449 }
450
451 /**
452 * Can you put a NULL in this column?
453 *
454 * @param column the first column is 1, the second is 2...
455 * @return one of the columnNullable values
456 * @throws java.sql.SQLException if a database access error occurs
457 */
458 public int isNullable(int column) throws java.sql.SQLException {
459 if (!getField(column).isNotNull()) {
460 return java.sql.ResultSetMetaData.columnNullable;
461 } else {
462 return java.sql.ResultSetMetaData.columnNoNulls;
463 }
464 }
465
466 /**
467 * What is a column's number of decimal digits.
468 *
469 * @param column the first column is 1, the second is 2...
470 * @return the precision
471 * @throws java.sql.SQLException if a database access error occurs
472 */
473 public int getPrecision(int column) throws java.sql.SQLException {
474 Field f = getField(column);
475
476 if (isDecimalType(f.getSQLType())) {
477 if (f.getDecimals() > 0) {
478 return (int)(f.getLength() - 1 + f.getPrecisionAdjustFactor());
479 }
480
481 return (int)(f.getLength() + f.getPrecisionAdjustFactor());
482 }
483
484 switch (f.getMysqlType()) {
485 case MysqlDefs.FIELD_TYPE_TINY_BLOB:
486 case MysqlDefs.FIELD_TYPE_BLOB:
487 case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB:
488 case MysqlDefs.FIELD_TYPE_LONG_BLOB:
489 return (int)f.getLength(); // this may change in the future
490 // for now, the server only
491 // returns FIELD_TYPE_BLOB for _all_
492 // BLOB types, but varying lengths
493 // indicating the _maximum_ size
494 // for each BLOB type.
495 default:
496 return (int)f.getLength();
497
498 }
499 }
500
501 /**
502 * Is the column definitely not writable?
503 *
504 * @param column the first column is 1, the second is 2, etc.
505 * @return true if so
506 * @throws java.sql.SQLException if a database access error occurs
507 */
508 public boolean isReadOnly(int column) throws java.sql.SQLException {
509 return getField(column).isReadOnly();
510 }
511
512 /**
513 * What is a column's number of digits to the right of the
514 * decimal point?
515 *
516 * @param column the first column is 1, the second is 2...
517 * @return the scale
518 * @throws java.sql.SQLException if a database access error occurs
519 */
520 public int getScale(int column) throws java.sql.SQLException {
521 Field f = getField(column);
522
523 if (isDecimalType(f.getSQLType())) {
524 return f.getDecimals();
525 }
526
527 return 0;
528 }
529
530 /**
531 * What is a column's table's schema? This relies on us knowing
532 * the table name.
533 *
534 * The JDBC specification allows us to return "" if this is not
535 * applicable.
536 *
537 * @param column the first column is 1, the second is 2...
538 * @return the Schema
539 * @throws java.sql.SQLException if a database access error occurs
540 */
541 public String getSchemaName(int column) throws java.sql.SQLException {
542 return "";
543 }
544
545 /**
546 * Can the column be used in a WHERE clause? Basically for
547 * this, I split the functions into two types: recognised
548 * types (which are always useable), and OTHER types (which
549 * may or may not be useable). The OTHER types, for now, I
550 * will assume they are useable. We should really query the
551 * catalog to see if they are useable.
552 *
553 * @param column the first column is 1, the second is 2...
554 * @return true if they can be used in a WHERE clause
555 * @throws java.sql.SQLException if a database access error occurs
556 */
557 public boolean isSearchable(int column) throws java.sql.SQLException {
558 return true;
559 }
560
561 /**
562 * Is the column a signed number?
563 *
564 * @param column the first column is 1, the second is 2...
565 * @return true if so
566 * @throws java.sql.SQLException if a database access error occurs
567 */
568 public boolean isSigned(int column) throws java.sql.SQLException {
569 Field f = getField(column);
570 int sqlType = f.getSQLType();
571
572 switch (sqlType) {
573 case Types.TINYINT:
574 case Types.SMALLINT:
575 case Types.INTEGER:
576 case Types.BIGINT:
577 case Types.FLOAT:
578 case Types.REAL:
579 case Types.DOUBLE:
580 case Types.NUMERIC:
581 case Types.DECIMAL:
582 return !f.isUnsigned();
583
584 case Types.DATE:
585 case Types.TIME:
586 case Types.TIMESTAMP:
587 return false;
588
589 default:
590 return false;
591 }
592 }
593
594 /**
595 * Whats a column's table's name?
596 *
597 * @param column the first column is 1, the second is 2...
598 * @return column name, or "" if not applicable
599 * @throws java.sql.SQLException if a database access error occurs
600 */
601 public String getTableName(int column) throws java.sql.SQLException {
602 return getField(column).getTableName();
603 }
604
605 /**
606 * Is it possible for a write on the column to succeed?
607 *
608 * @param column the first column is 1, the second is 2, etc.
609 * @return true if so
610 * @throws java.sql.SQLException if a database access error occurs
611 */
612 public boolean isWritable(int column) throws java.sql.SQLException {
613 return !isReadOnly(column);
614 }
615
616 // *********************************************************************
617 //
618 // END OF PUBLIC INTERFACE
619 //
620 // *********************************************************************
621
622 /**
623 * Returns the field instance for the given column index
624 *
625 * @param columnIndex the column number to retrieve a field instance for
626 * @return the field instance for the given column index
627 *
628 * @throws java.sql.SQLException if an error occurs
629 */
630 protected Field getField(int columnIndex) throws java.sql.SQLException {
631 if ((columnIndex < 1) || (columnIndex > fields.length)) {
632 throw new java.sql.SQLException("Column index out of range.",
633 SQLError.SQL_STATE_INVALID_COLUMN_NUMBER);
634 }
635
636 return fields[columnIndex - 1];
637 }
638
639 /**
640 * Checks if the SQL Type is a Decimal/Number Type
641 * @param type SQL Type
642 */
643 private static final boolean isDecimalType(int type) {
644 switch (type) {
645 case Types.BIT:
646 case Types.TINYINT:
647 case Types.SMALLINT:
648 case Types.INTEGER:
649 case Types.BIGINT:
650 case Types.FLOAT:
651 case Types.REAL:
652 case Types.DOUBLE:
653 case Types.NUMERIC:
654 case Types.DECIMAL:
655 return true;
656 }
657
658 return false;
659 }
660 }