001: /* Copyright (c) 2001-2005, The HSQL Development Group
002: * All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * Redistributions of source code must retain the above copyright notice, this
008: * list of conditions and the following disclaimer.
009: *
010: * Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * Neither the name of the HSQL Development Group nor the names of its
015: * contributors may be used to endorse or promote products derived from this
016: * software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
022: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
026: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030:
031: package org.hsqldb;
032:
033: import java.util.Locale;
034:
035: import org.hsqldb.resources.BundleHandler;
036: import org.hsqldb.store.ValuePool;
037:
038: /**
039: * Provides extended information about HSQLDB tables and their
040: * columns/indices. <p>
041: *
042: * @author boucherb@users
043: * @version 1.8.0
044: * @since 1.7.2
045: */
046: final class DITableInfo {
047:
048: // related to DatabaseMetaData
049: int bestRowTemporary = 0;
050: int bestRowTransaction = 1;
051: int bestRowSession = 2;
052: int bestRowUnknown = 0;
053: int bestRowNotPseudo = 1;
054: static final short tableIndexOther = 3;
055:
056: /** Used in buffer size and character octet length determinations. */
057: private static final int HALF_MAX_INT = Integer.MAX_VALUE >>> 1;
058:
059: /** BundleHandler id for column remarks resource bundle. */
060: private int hnd_column_remarks = -1;
061:
062: /** BundleHandler id for table remarks resource bundle. */
063: private int hnd_table_remarks = -1;
064:
065: /** The Table object upon which this object is reporting. */
066: private Table table;
067:
068: /** Provides intrinsic type infformation support. */
069: private static final DITypeInfo ti = new DITypeInfo();
070:
071: /**
072: * Creates a new DITableInfo object with the default Locale and reporting
073: * on no table. It is absolutely essential the a valid Table object is
074: * assigned to this object, using the setTable method, before any Table,
075: * Column or Index oriented value retrieval methods are called; this class
076: * contains no assertions or exception handling related to a null or
077: * invalid table member attribute.
078: */
079: DITableInfo() {
080:
081: /** @todo fredt - remove from here: should be set in a database-wide context */
082: setLocale(Locale.getDefault());
083: }
084:
085: /**
086: * Sets the Locale for table and column remarks. <p>
087: *
088: * @param l The Locale object
089: */
090: void setLocale(Locale l) {
091:
092: Locale oldLocale;
093:
094: synchronized (BundleHandler.class) {
095: oldLocale = BundleHandler.getLocale();
096:
097: BundleHandler.setLocale(l);
098:
099: hnd_column_remarks = BundleHandler.getBundleHandle(
100: "column-remarks", null);
101: hnd_table_remarks = BundleHandler.getBundleHandle(
102: "table-remarks", null);
103:
104: BundleHandler.setLocale(oldLocale);
105: }
106: }
107:
108: /**
109: * Retrieves whether the best row identifier column is
110: * a pseudo column, like an Oracle ROWID. <p>
111: *
112: * Currently, this always returns an Integer whose value is
113: * DatabaseMetaData.bestRowNotPseudo, as HSQLDB does not support
114: * pseudo columns such as ROWID. <p>
115: *
116: * @return whether the best row identifier column is
117: * a pseudo column
118: */
119: Integer getBRIPseudo() {
120: return ValuePool.getInt(bestRowNotPseudo);
121: }
122:
123: /**
124: * Retrieves the scope of the best row identifier. <p>
125: *
126: * This implements the rules described in
127: * DatabaseInformationMain.SYSTEM_BESTROWIDENTIFIER. <p>
128: *
129: * @return the scope of the best row identifier
130: */
131: Integer getBRIScope() {
132: return (table.isWritable()) ? ValuePool
133: .getInt(bestRowTemporary) : ValuePool
134: .getInt(bestRowSession);
135: }
136:
137: /**
138: * Retrieves, if definitely known, the transfer size for values of the
139: * specified column, in bytes. <p>
140: *
141: * @param i zero-based column index
142: * @return the transfer size for values of the
143: * specified column, in bytes
144: */
145: Integer getColBufLen(int i) {
146:
147: int size;
148: int type;
149: Column column;
150:
151: column = table.getColumn(i);
152: type = column.getDIType();
153:
154: switch (type) {
155:
156: case Types.CHAR:
157: case Types.CLOB:
158: case Types.LONGVARCHAR:
159: case Types.VARCHAR: {
160: size = column.getSize();
161:
162: if (size == 0) {
163: } else if (size > HALF_MAX_INT) {
164: size = 0;
165: } else {
166: size = 2 * size;
167: }
168:
169: break;
170: }
171: case Types.BINARY:
172: case Types.BLOB:
173: case Types.LONGVARBINARY:
174: case Types.VARBINARY: {
175: size = column.getSize();
176:
177: break;
178: }
179: case Types.BIGINT:
180: case Types.DOUBLE:
181: case Types.FLOAT:
182: case Types.DATE:
183: case Types.REAL:
184: case Types.TIME: {
185: size = 8;
186:
187: break;
188: }
189: case Types.TIMESTAMP: {
190: size = 12;
191:
192: break;
193: }
194: case Types.INTEGER:
195: case Types.SMALLINT:
196: case Types.TINYINT: {
197: size = 4;
198:
199: break;
200: }
201: case Types.BOOLEAN: {
202: size = 1;
203:
204: break;
205: }
206: default: {
207: size = 0;
208:
209: break;
210: }
211: }
212:
213: return (size > 0) ? ValuePool.getInt(size) : null;
214: }
215:
216: /**
217: * Retrieves the declared size, in bytes, for character-valued
218: * columns. <p>
219: *
220: * If the size cannot be represented in the range [0,Integer.MAX_VALUE],
221: * this returns null. <p>
222: *
223: * @param i zero-based column index
224: * @return the size, in bytes, for character-valued columns
225: */
226: Integer getColCharOctLen(int i) {
227:
228: int size;
229: int type;
230: Column column;
231:
232: column = table.getColumn(i);
233: type = column.getDIType();
234:
235: switch (type) {
236:
237: case Types.CHAR:
238: case Types.CLOB:
239: case Types.LONGVARCHAR:
240: case Types.VARCHAR: {
241: size = column.getSize();
242:
243: if (size == 0) {
244: } else if (size > HALF_MAX_INT) {
245: size = 0;
246: } else {
247: size = 2 * size;
248: }
249:
250: break;
251: }
252: default: {
253: size = 0;
254:
255: break;
256: }
257: }
258:
259: return (size == 0) ? null : ValuePool.getInt(size);
260: }
261:
262: /**
263: * Retrieves the SQL data type code for the specified column. <p>
264: *
265: * @param i zero-based column index
266: * @return the SQL data type code for the specified column
267: */
268: Integer getColDataType(int i) {
269: return ValuePool.getInt(table.getColumn(i).getDIType());
270: }
271:
272: /**
273: * Retrieves the SQL data type name for the specified column. <p>
274: *
275: * @param i zero-based column index
276: * @return the SQL data type name for the specified column
277: */
278: String getColDataTypeName(int i) {
279:
280: Column column = table.getColumn(i);
281:
282: ti.setTypeCode(column.getDIType());
283: ti.setTypeSub(column.getDITypeSub());
284:
285: return ti.getTypeName();
286: }
287:
288: /**
289: * Retrieves the HSQLDB data subtype code for the specified column. <p>
290: *
291: * @param i zero-based column index
292: * @return the HSQLDB data subtype code for the specified column
293: */
294: Integer getColDataTypeSub(int i) {
295: return ValuePool.getInt(table.getColumn(i).getDITypeSub());
296: }
297:
298: /**
299: * Retrieves the declared default value expression for the column. <p>
300: *
301: * @param i zero-based column index
302: * @return the declared default value expression for the column
303: */
304: String getColDefault(int i) {
305: return table.getColumn(i).getDefaultDDL();
306: }
307:
308: /**
309: * Retrieves whether the specified column is the identity column for
310: * the table. <p>
311: *
312: * @param i zero-based column index
313: * @return whether the specified column is the identity column for
314: * the table.
315: */
316: Boolean getColIsIdentity(int i) {
317: return ValuePool.getBoolean(table.getColumn(i).isIdentity());
318: }
319:
320: /**
321: * Retrieves whether the specified column is nullable. <p>
322: *
323: * If the column is nullable, "YES" is retrieved, else "NO". <p>
324: *
325: * @param i zero-based column index
326: * @return the nullability of the specified column
327: */
328: String getColIsNullable(int i) {
329:
330: Column column = table.getColumn(i);
331:
332: return (column.isNullable() && !column.isIdentity()) ? "YES"
333: : "NO";
334: }
335:
336: /**
337: * Retrieves the simple name of the specified column. <p>
338: *
339: * @param i zero-based column index
340: * @return the simple name of the specified column.
341: */
342: String getColName(int i) {
343: return table.getColumn(i).columnName.name;
344: }
345:
346: /**
347: * Retrieves the specified column's nullablility. <p>
348: *
349: * @param i zero-based column index
350: * @return the specified column's nullablilit
351: */
352: Integer getColNullability(int i) {
353:
354: Column column = table.getColumn(i);
355:
356: return (column.isNullable() && !column.isIdentity()) ? ValuePool
357: .getInt(DITypeInfo.columnNullable)
358: : ValuePool.getInt(DITypeInfo.columnNoNulls);
359: }
360:
361: /**
362: * Retrieves the number base that should be used to interpret the
363: * specified column's numeric precision, as reported by getColSize(int).
364: *
365: * @param i zero-based column index
366: * @return the number base that should be used to
367: * interpret the column's numeric precision,
368: * as reported by getColSize(int).
369: */
370: Integer getColPrecRadix(int i) {
371:
372: ti.setTypeCode(table.getColumn(i).getDIType());
373:
374: return ti.getNumPrecRadix();
375: }
376:
377: /**
378: * Retrieves the remarks, if any, recorded against the specified
379: * column. <p>
380: *
381: * @param i zero-based column index
382: * @return the remarks recorded against the specified column.
383: */
384: String getColRemarks(int i) {
385:
386: String key;
387:
388: if (table.getTableType() != Table.SYSTEM_TABLE) {
389: return null;
390: }
391:
392: key = getName() + "_" + getColName(i);
393:
394: return BundleHandler.getString(hnd_column_remarks, key);
395: }
396:
397: /**
398: * Retrieves the declared (but not currently enforced) or implicit fixed
399: * number of digits to the right of the decimal point for exact numeric
400: * types.
401: *
402: * If the column's type precludes scale declaration, null is returned.
403: *
404: * @param i zero-based column index
405: * @return the fixed number of digits to the right of the decimal point
406: * for exact numeric types.
407: */
408: Integer getColScale(int i) {
409:
410: Column column;
411: int type;
412:
413: column = table.getColumn(i);
414: type = column.getDIType();
415:
416: return Types.acceptsScaleCreateParam(type) ? ValuePool
417: .getInt(column.getScale()) : null;
418: }
419:
420: /**
421: * Retrieves null (not implemented). <p>
422: *
423: * @param i zero-based column index
424: * @return null (not implemented)
425: */
426: String getColScopeCat(int i) {
427: return null;
428: }
429:
430: /**
431: * Retrieves null (not implemented). <p>
432: *
433: * @param i zero-based column index
434: * @return null (not implemented)
435: */
436: String getColScopeSchem(int i) {
437: return null;
438: }
439:
440: /**
441: * Retrieves null (not implemented). <p>
442: *
443: * @param i zero-based column index
444: * @return null (not implemented)
445: */
446: String getColScopeTable(int i) {
447: return null;
448: }
449:
450: /**
451: * Retrieves either the declared or maximum length/precision for
452: * the specified column, if its type allows a precision/length
453: * declaration, else null. <p>
454: *
455: * @param i zero-based column index
456: * @return the declared or maximum length/precision for
457: * the specified column
458: */
459: Integer getColSize(int i) {
460:
461: Column column;
462: int type;
463: int size;
464:
465: column = table.getColumn(i);
466: type = column.getDIType();
467:
468: if (!Types.acceptsPrecisionCreateParam(type)) {
469: return null;
470: }
471:
472: size = column.getSize();
473:
474: if (size > 0) {
475: return ValuePool.getInt(size);
476: } else {
477: ti.setTypeCode(type);
478:
479: return ti.getPrecision();
480: }
481: }
482:
483: /**
484: * Retrieves the SQL CLI data type code for the specified column. <p>
485: *
486: * @param i zero-based column index
487: * @return the SQL CLI data type code for the specified column
488: */
489: Integer getColSqlDataType(int i) {
490:
491: ti.setTypeCode(table.getColumn(i).getDIType());
492:
493: return ti.getSqlDataType();
494: }
495:
496: /**
497: * Retrieves the SQL CLI datetime subtype for the specified column. <p>
498: *
499: * @param i zero-based column index
500: * @return the SQL CLI datetime subtype for the specified column
501: */
502: Integer getColSqlDateTimeSub(int i) {
503:
504: ti.setTypeCode(table.getColumn(i).getDIType());
505:
506: return ti.getSqlDateTimeSub();
507: }
508:
509: /**
510: * Retrieves the full data source descriptor for [TEMP] TEXT tables. <p>
511: *
512: * @return the full data source descriptor
513: */
514: String getDataSource() {
515: return table.getDataSource();
516: }
517:
518: /**
519: * Retrieves the HSQLDB-specific type of the table. <p>
520: *
521: * @return the HSQLDB-specific type of the table
522: */
523: String getHsqlType() {
524:
525: switch (table.getTableType()) {
526:
527: case Table.MEMORY_TABLE:
528: case Table.TEMP_TABLE:
529: case Table.SYSTEM_TABLE:
530: return "MEMORY";
531:
532: case Table.CACHED_TABLE:
533: return "CACHED";
534:
535: case Table.TEMP_TEXT_TABLE:
536: case Table.TEXT_TABLE:
537: return "TEXT";
538:
539: case Table.VIEW:
540: default:
541: return null;
542: }
543: }
544:
545: /**
546: * Retrieves null (not implemented). <p>
547: *
548: * @param i zero-based index specifier
549: * @return null (not implemented)
550: */
551: Integer getIndexCardinality(int i) {
552: return null;
553: }
554:
555: /**
556: * Retrieves the sort-direction for the specified column in the
557: * specified index. <p>
558: *
559: * @param i zero-based index specifier
560: * @param columnPosition zero-based ordinal position of column in index
561: * @return the sort-direction for the specified column in the
562: * specified index
563: */
564: String getIndexColDirection(int i, int columnPosition) {
565:
566: // so far, hsqldb only supports completely ascending indexes
567: return "A";
568: }
569:
570: /**
571: * Retrieves an array map from the zero-based ordinal positions of the
572: * columns in the specfied Index to the zero-based ordinal positions of
573: * the same columns in the table. <p>
574: *
575: * @param i zero-based index specifier
576: * @return an array map from the zero-based ordinal positions of
577: * the columns in the specfied Index to the zero-based
578: * ordinal positions of the same columns in the table
579: */
580: int[] getIndexColumns(int i) {
581: return table.getIndex(i).getColumns();
582: }
583:
584: /**
585: * Retrieves the simple name of the specified Index. <p>
586: *
587: * @param i zero-based index specifier
588: * @return the simple name of the specified Index
589: */
590: String getIndexName(int i) {
591: return table.getIndex(i).getName().name;
592: }
593:
594: /**
595: * Retrieves null (not implemented). <p>
596: *
597: * @param i zero-based index specifier
598: * @return null (not implemented)
599: */
600: Integer getIndexRowCardinality(int i) {
601: return null;
602: }
603:
604: /**
605: * Retrieves the DatabaseMetaData type code of the specified Index. <p>
606: *
607: * @param i zero-based index specifier
608: * @return the DatabaseMetaData type code of the specified Index
609: */
610: Integer getIndexType(int i) {
611: return ValuePool.getInt(tableIndexOther);
612: }
613:
614: /**
615: * Retrieves the number of visible columns in the specified Index. That
616: * is, this retrieves one less than the physical number of columns if the
617: * table maintains an internal primary index on an internal identity
618: * column, as is the case when the table has no declared primary key or
619: * identity column. <p>
620: *
621: * @param i zero-based index specifier
622: * @return the number of visible columns in the specified Index
623: */
624: int getIndexVisibleColumns(int i) {
625: return table.getIndex(i).getVisibleColumns();
626: }
627:
628: /**
629: * Retrieves the simple name of the table. <p>
630: *
631: * @return the simple name of the table
632: */
633: String getName() {
634: return table.getName().name;
635: }
636:
637: /**
638: * Retrieves the value of the next automatically assigned identity. <p>
639: *
640: * Be aware that this is not necessarily the value that will be assigned
641: * to the identity column during the next insert or update. This value is
642: * used if NULL is either implicitly or explicity assigned. <p>
643: *
644: * @return the value of the next automatically assigned identity
645: */
646: Long getNextIdentity() {
647:
648: Index pi;
649:
650: if (table.identityColumn < 0) {
651: return null;
652: }
653:
654: return ValuePool.getLong(table.identitySequence.peek());
655: }
656:
657: /**
658: * Retrieves the remarks (if any) recorded against the Table. <p>
659: *
660: * @return the remarks recorded against the Table
661: */
662: String getRemark() {
663:
664: return (table.getTableType() == Table.SYSTEM_TABLE) ? BundleHandler
665: .getString(hnd_table_remarks, getName())
666: : null;
667: }
668:
669: /**
670: * Retrieves the standard JDBC type of the table. <p>
671: *
672: * "TABLE" for user-defined tables, "VIEW" for user-defined views,
673: * and so on.
674: *
675: * @return the standard JDBC type of the table
676: */
677: String getStandardType() {
678:
679: switch (table.getTableType()) {
680:
681: case Table.VIEW:
682: return "VIEW";
683:
684: case Table.TEMP_TABLE:
685: case Table.TEMP_TEXT_TABLE:
686: return "GLOBAL TEMPORARY";
687:
688: case Table.SYSTEM_TABLE:
689: return "SYSTEM TABLE";
690:
691: default:
692: return "TABLE";
693: }
694: }
695:
696: /**
697: * Retrieves the Table object on which this object is currently
698: * reporting. <p>
699: *
700: * @return the Table object on which this object
701: * is currently reporting
702: */
703: Table getTable() {
704: return this .table;
705: }
706:
707: /**
708: * Retrieves, for [TEMP] TEXT tables, whether the table's data source
709: * descriptor requests descending read semantics. That is, when this
710: * value is true, it indicate that the text file is to be read from
711: * the bottom up. <p>
712: *
713: * @return whether the table's data source
714: * descriptor requests descending
715: * read semantics
716: */
717: Boolean isDataSourceDescending() {
718: return ValuePool.getBoolean(table.isDescDataSource());
719: }
720:
721: /**
722: * Retrieves whether the specified Index is non-unique. <p>
723: *
724: * @param i zero-based index specifier
725: * @return whether the specified Index is non-unique
726: */
727: Boolean isIndexNonUnique(int i) {
728: return ValuePool.getBoolean(!table.getIndex(i).isUnique());
729: }
730:
731: /**
732: * Retrieves whether the table is in data read-only mode. This value does
733: * not reflect the various read-only modes of the database or the
734: * read-only mode of the connection. <p>
735: *
736: * @return whether the table is in data read-only mode
737: */
738: Boolean isReadOnly() {
739: return ValuePool.getBoolean(table.isDataReadOnly());
740: }
741:
742: /**
743: * Assigns the Table object on which this object is to report. <p>
744: *
745: * @param table the Table object on which this object is to report
746: */
747: void setTable(Table table) {
748: this.table = table;
749: }
750: }
|