001: /*-------------------------------------------------------------------------
002: *
003: * Copyright (c) 2004-2005, PostgreSQL Global Development Group
004: *
005: * IDENTIFICATION
006: * $PostgreSQL: pgjdbc/org/postgresql/jdbc2/AbstractJdbc2ResultSetMetaData.java,v 1.20 2005/12/04 20:14:28 jurka Exp $
007: *
008: *-------------------------------------------------------------------------
009: */
010: package org.postgresql.jdbc2;
011:
012: import org.postgresql.PGResultSetMetaData;
013: import org.postgresql.core.*;
014: import org.postgresql.util.PSQLException;
015: import org.postgresql.util.PSQLState;
016: import java.sql.*;
017: import java.util.Hashtable;
018: import org.postgresql.util.GT;
019:
020: public abstract class AbstractJdbc2ResultSetMetaData implements
021: PGResultSetMetaData {
022: protected final BaseConnection connection;
023: protected final Field[] fields;
024:
025: private Hashtable tableNameCache;
026: private Hashtable schemaNameCache;
027:
028: /*
029: * Initialise for a result with a tuple set and
030: * a field descriptor set
031: *
032: * @param fields the array of field descriptors
033: */
034: public AbstractJdbc2ResultSetMetaData(BaseConnection connection,
035: Field[] fields) {
036: this .connection = connection;
037: this .fields = fields;
038: }
039:
040: /*
041: * Whats the number of columns in the ResultSet?
042: *
043: * @return the number
044: * @exception SQLException if a database access error occurs
045: */
046: public int getColumnCount() throws SQLException {
047: return fields.length;
048: }
049:
050: /*
051: * Is the column automatically numbered (and thus read-only)
052: * I believe that PostgreSQL does not support this feature.
053: *
054: * @param column the first column is 1, the second is 2...
055: * @return true if so
056: * @exception SQLException if a database access error occurs
057: */
058: public boolean isAutoIncrement(int column) throws SQLException {
059: Field field = getField(column);
060: return field.getAutoIncrement(connection);
061: }
062:
063: /*
064: * Does a column's case matter? ASSUMPTION: Any field that is
065: * not obviously case insensitive is assumed to be case sensitive
066: *
067: * @param column the first column is 1, the second is 2...
068: * @return true if so
069: * @exception SQLException if a database access error occurs
070: */
071: public boolean isCaseSensitive(int column) throws SQLException {
072: Field field = getField(column);
073: return TypeInfoCache.isCaseSensitive(field.getOID());
074: }
075:
076: /*
077: * Can the column be used in a WHERE clause? Basically for
078: * this, I split the functions into two types: recognised
079: * types (which are always useable), and OTHER types (which
080: * may or may not be useable). The OTHER types, for now, I
081: * will assume they are useable. We should really query the
082: * catalog to see if they are useable.
083: *
084: * @param column the first column is 1, the second is 2...
085: * @return true if they can be used in a WHERE clause
086: * @exception SQLException if a database access error occurs
087: */
088: public boolean isSearchable(int column) throws SQLException {
089: return true;
090: }
091:
092: /*
093: * Is the column a cash value? 6.1 introduced the cash/money
094: * type, which haven't been incorporated as of 970414, so I
095: * just check the type name for both 'cash' and 'money'
096: *
097: * @param column the first column is 1, the second is 2...
098: * @return true if its a cash column
099: * @exception SQLException if a database access error occurs
100: */
101: public boolean isCurrency(int column) throws SQLException {
102: String type_name = getPGType(column);
103:
104: return type_name.equals("cash") || type_name.equals("money");
105: }
106:
107: /*
108: * Indicates the nullability of values in the designated column.
109: *
110: * @param column the first column is 1, the second is 2...
111: * @return one of the columnNullable values
112: * @exception SQLException if a database access error occurs
113: */
114: public int isNullable(int column) throws SQLException {
115: Field field = getField(column);
116: return field.getNullable(connection);
117: }
118:
119: /*
120: * Is the column a signed number? In PostgreSQL, all numbers
121: * are signed, so this is trivial. However, strings are not
122: * signed (duh!)
123: *
124: * @param column the first column is 1, the second is 2...
125: * @return true if so
126: * @exception SQLException if a database access error occurs
127: */
128: public boolean isSigned(int column) throws SQLException {
129: Field field = getField(column);
130: return TypeInfoCache.isSigned(field.getOID());
131: }
132:
133: /*
134: * What is the column's normal maximum width in characters?
135: *
136: * @param column the first column is 1, the second is 2, etc.
137: * @return the maximum width
138: * @exception SQLException if a database access error occurs
139: */
140: public int getColumnDisplaySize(int column) throws SQLException {
141: Field field = getField(column);
142: return TypeInfoCache.getDisplaySize(field.getOID(), field
143: .getMod());
144: }
145:
146: /*
147: * @param column the first column is 1, the second is 2, etc.
148: * @return the column label
149: * @exception SQLException if a database access error occurs
150: */
151: public String getColumnLabel(int column) throws SQLException {
152: Field field = getField(column);
153: return field.getColumnLabel();
154: }
155:
156: /*
157: * What's a column's name?
158: *
159: * @param column the first column is 1, the second is 2, etc.
160: * @return the column name
161: * @exception SQLException if a database access error occurs
162: */
163: public String getColumnName(int column) throws SQLException {
164: return getColumnLabel(column);
165: }
166:
167: public String getBaseColumnName(int column) throws SQLException {
168: Field field = getField(column);
169: return field.getColumnName(connection);
170: }
171:
172: /*
173: * @param column the first column is 1, the second is 2...
174: * @return the Schema Name
175: * @exception SQLException if a database access error occurs
176: */
177: public String getSchemaName(int column) throws SQLException {
178: return "";
179: }
180:
181: public String getBaseSchemaName(int column) throws SQLException {
182: Field field = getField(column);
183: if (field.getTableOid() == 0) {
184: return "";
185: }
186: Integer tableOid = new Integer(field.getTableOid());
187: if (schemaNameCache == null) {
188: schemaNameCache = new Hashtable();
189: }
190: String schemaName = (String) schemaNameCache.get(tableOid);
191: if (schemaName != null) {
192: return schemaName;
193: } else {
194: ResultSet res = null;
195: PreparedStatement ps = null;
196: try {
197: String sql = "SELECT n.nspname FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n WHERE n.oid = c.relnamespace AND c.oid = ?;";
198: ps = ((Connection) connection).prepareStatement(sql);
199: ps.setInt(1, tableOid.intValue());
200: res = ps.executeQuery();
201: schemaName = "";
202: if (res.next()) {
203: schemaName = res.getString(1);
204: }
205: schemaNameCache.put(tableOid, schemaName);
206: return schemaName;
207: } finally {
208: if (res != null)
209: res.close();
210: if (ps != null)
211: ps.close();
212: }
213: }
214: }
215:
216: /*
217: * What is a column's number of decimal digits.
218: *
219: * @param column the first column is 1, the second is 2...
220: * @return the precision
221: * @exception SQLException if a database access error occurs
222: */
223: public int getPrecision(int column) throws SQLException {
224: Field field = getField(column);
225: return TypeInfoCache.getPrecision(field.getOID(), field
226: .getMod());
227: }
228:
229: /*
230: * What is a column's number of digits to the right of the
231: * decimal point?
232: *
233: * @param column the first column is 1, the second is 2...
234: * @return the scale
235: * @exception SQLException if a database access error occurs
236: */
237: public int getScale(int column) throws SQLException {
238: Field field = getField(column);
239: return TypeInfoCache.getScale(field.getOID(), field.getMod());
240: }
241:
242: /*
243: * @param column the first column is 1, the second is 2...
244: * @return column name, or "" if not applicable
245: * @exception SQLException if a database access error occurs
246: */
247: public String getTableName(int column) throws SQLException {
248: return "";
249: }
250:
251: public String getBaseTableName(int column) throws SQLException {
252: Field field = getField(column);
253: if (field.getTableOid() == 0) {
254: return "";
255: }
256: Integer tableOid = new Integer(field.getTableOid());
257: if (tableNameCache == null) {
258: tableNameCache = new Hashtable();
259: }
260: String tableName = (String) tableNameCache.get(tableOid);
261: if (tableName != null) {
262: return tableName;
263: } else {
264: ResultSet res = null;
265: PreparedStatement ps = null;
266: try {
267: ps = ((Connection) connection)
268: .prepareStatement("SELECT relname FROM pg_catalog.pg_class WHERE oid = ?");
269: ps.setInt(1, tableOid.intValue());
270: res = ps.executeQuery();
271: tableName = "";
272: if (res.next()) {
273: tableName = res.getString(1);
274: }
275: tableNameCache.put(tableOid, tableName);
276: return tableName;
277: } finally {
278: if (res != null)
279: res.close();
280: if (ps != null)
281: ps.close();
282: }
283: }
284: }
285:
286: /*
287: * What's a column's table's catalog name? As with getSchemaName(),
288: * we can say that if getTableName() returns n/a, then we can too -
289: * otherwise, we need to work on it.
290: *
291: * @param column the first column is 1, the second is 2...
292: * @return catalog name, or "" if not applicable
293: * @exception SQLException if a database access error occurs
294: */
295: public String getCatalogName(int column) throws SQLException {
296: return "";
297: }
298:
299: /*
300: * What is a column's SQL Type? (java.sql.Type int)
301: *
302: * @param column the first column is 1, the second is 2, etc.
303: * @return the java.sql.Type value
304: * @exception SQLException if a database access error occurs
305: * @see org.postgresql.Field#getSQLType
306: * @see java.sql.Types
307: */
308: public int getColumnType(int column) throws SQLException {
309: return getSQLType(column);
310: }
311:
312: /*
313: * Whats is the column's data source specific type name?
314: *
315: * @param column the first column is 1, the second is 2, etc.
316: * @return the type name
317: * @exception SQLException if a database access error occurs
318: */
319: public String getColumnTypeName(int column) throws SQLException {
320: return getPGType(column);
321: }
322:
323: /*
324: * Is the column definitely not writable? In reality, we would
325: * have to check the GRANT/REVOKE stuff for this to be effective,
326: * and I haven't really looked into that yet, so this will get
327: * re-visited.
328: *
329: * @param column the first column is 1, the second is 2, etc.
330: * @return true if so
331: * @exception SQLException if a database access error occurs
332: */
333: public boolean isReadOnly(int column) throws SQLException {
334: return false;
335: }
336:
337: /*
338: * Is it possible for a write on the column to succeed? Again, we
339: * would in reality have to check the GRANT/REVOKE stuff, which
340: * I haven't worked with as yet. However, if it isn't ReadOnly, then
341: * it is obviously writable.
342: *
343: * @param column the first column is 1, the second is 2, etc.
344: * @return true if so
345: * @exception SQLException if a database access error occurs
346: */
347: public boolean isWritable(int column) throws SQLException {
348: return !isReadOnly(column);
349: }
350:
351: /*
352: * Will a write on this column definately succeed? Hmmm...this
353: * is a bad one, since the two preceding functions have not been
354: * really defined. I cannot tell is the short answer. I thus
355: * return isWritable() just to give us an idea.
356: *
357: * @param column the first column is 1, the second is 2, etc..
358: * @return true if so
359: * @exception SQLException if a database access error occurs
360: */
361: public boolean isDefinitelyWritable(int column) throws SQLException {
362: return false;
363: }
364:
365: // ********************************************************
366: // END OF PUBLIC INTERFACE
367: // ********************************************************
368:
369: /*
370: * For several routines in this package, we need to convert
371: * a columnIndex into a Field[] descriptor. Rather than do
372: * the same code several times, here it is.
373: *
374: * @param columnIndex the first column is 1, the second is 2...
375: * @return the Field description
376: * @exception SQLException if a database access error occurs
377: */
378: protected Field getField(int columnIndex) throws SQLException {
379: if (columnIndex < 1 || columnIndex > fields.length)
380: throw new PSQLException(
381: GT
382: .tr(
383: "The column index is out of range: {0}, number of columns: {1}.",
384: new Object[] {
385: new Integer(columnIndex),
386: new Integer(fields.length) }),
387: PSQLState.INVALID_PARAMETER_VALUE);
388: return fields[columnIndex - 1];
389: }
390:
391: protected String getPGType(int columnIndex) throws SQLException {
392: return connection.getPGType(getField(columnIndex).getOID());
393: }
394:
395: protected int getSQLType(int columnIndex) throws SQLException {
396: return connection.getSQLType(getField(columnIndex).getOID());
397: }
398:
399: // ** JDBC 2 Extensions **
400:
401: // This can hook into our PG_Object mechanism
402: /**
403: * Returns the fully-qualified name of the Java class whose instances
404: * are manufactured if the method <code>ResultSet.getObject</code>
405: * is called to retrieve a value from the column.
406: *
407: * <code>ResultSet.getObject</code> may return a subclass of the class
408: * returned by this method.
409: *
410: * @param column the first column is 1, the second is 2, ...
411: * @return the fully-qualified name of the class in the Java programming
412: * language that would be used by the method
413: * <code>ResultSet.getObject</code> to retrieve the value in the specified
414: * column. This is the class name used for custom mapping.
415: * @exception SQLException if a database access error occurs
416: */
417: public String getColumnClassName(int column) throws SQLException {
418: Field field = getField(column);
419: String result = connection.getJavaClass(field.getOID());
420:
421: if (result != null)
422: return result;
423:
424: int sqlType = getSQLType(column);
425: switch (sqlType) {
426: case Types.ARRAY:
427: return ("java.sql.Array");
428: default:
429: String type = getPGType(column);
430: if ("unknown".equals(type)) {
431: return ("java.lang.String");
432: }
433: return ("java.lang.Object");
434: }
435: }
436: }
|