Source code: postgresql/jdbc1/ResultSetMetaData.java
1 package postgresql.jdbc1;
2
3 // IMPORTANT NOTE: This file implements the JDBC 1 version of the driver.
4 // If you make any modifications to this file, you must make sure that the
5 // changes are also made (if relevent) to the related JDBC 2 class in the
6 // postgresql.jdbc2 package.
7
8 import java.lang.*;
9 import java.util.*;
10 import postgresql.*;
11 import postgresql.util.*;
12
13 // We explicitly import classes here as the original line:
14 //import java.sql.*;
15 // causes javac to get confused.
16 import java.sql.SQLException;
17 import java.sql.Types;
18
19 /**
20 * A ResultSetMetaData object can be used to find out about the types and
21 * properties of the columns in a ResultSet
22 *
23 * @see java.sql.ResultSetMetaData
24 */
25 public class ResultSetMetaData implements java.sql.ResultSetMetaData
26 {
27 Vector rows;
28 Field[] fields;
29
30 /**
31 * Initialise for a result with a tuple set and
32 * a field descriptor set
33 *
34 * @param rows the Vector of rows returned by the ResultSet
35 * @param fields the array of field descriptors
36 */
37 public ResultSetMetaData(Vector rows, Field[] fields)
38 {
39 this.rows = rows;
40 this.fields = fields;
41 }
42
43 /**
44 * Whats the number of columns in the ResultSet?
45 *
46 * @return the number
47 * @exception SQLException if a database access error occurs
48 */
49 public int getColumnCount() throws SQLException
50 {
51 return fields.length;
52 }
53
54 /**
55 * Is the column automatically numbered (and thus read-only)
56 * I believe that PostgreSQL does not support this feature.
57 *
58 * @param column the first column is 1, the second is 2...
59 * @return true if so
60 * @exception SQLException if a database access error occurs
61 */
62 public boolean isAutoIncrement(int column) throws SQLException
63 {
64 return false;
65 }
66
67 /**
68 * Does a column's case matter? ASSUMPTION: Any field that is
69 * not obviously case insensitive is assumed to be case sensitive
70 *
71 * @param column the first column is 1, the second is 2...
72 * @return true if so
73 * @exception SQLException if a database access error occurs
74 */
75 public boolean isCaseSensitive(int column) throws SQLException
76 {
77 int sql_type = getField(column).getSQLType();
78
79 switch (sql_type)
80 {
81 case Types.SMALLINT:
82 case Types.INTEGER:
83 case Types.FLOAT:
84 case Types.REAL:
85 case Types.DOUBLE:
86 case Types.DATE:
87 case Types.TIME:
88 case Types.TIMESTAMP:
89 return false;
90 default:
91 return true;
92 }
93 }
94
95 /**
96 * Can the column be used in a WHERE clause? Basically for
97 * this, I split the functions into two types: recognised
98 * types (which are always useable), and OTHER types (which
99 * may or may not be useable). The OTHER types, for now, I
100 * will assume they are useable. We should really query the
101 * catalog to see if they are useable.
102 *
103 * @param column the first column is 1, the second is 2...
104 * @return true if they can be used in a WHERE clause
105 * @exception SQLException if a database access error occurs
106 */
107 public boolean isSearchable(int column) throws SQLException
108 {
109 int sql_type = getField(column).getSQLType();
110
111 // This switch is pointless, I know - but it is a set-up
112 // for further expansion.
113 switch (sql_type)
114 {
115 case Types.OTHER:
116 return true;
117 default:
118 return true;
119 }
120 }
121
122 /**
123 * Is the column a cash value? 6.1 introduced the cash/money
124 * type, which haven't been incorporated as of 970414, so I
125 * just check the type name for both 'cash' and 'money'
126 *
127 * @param column the first column is 1, the second is 2...
128 * @return true if its a cash column
129 * @exception SQLException if a database access error occurs
130 */
131 public boolean isCurrency(int column) throws SQLException
132 {
133 String type_name = getField(column).getTypeName();
134
135 return type_name.equals("cash") || type_name.equals("money");
136 }
137
138 /**
139 * Can you put a NULL in this column? I think this is always
140 * true in 6.1's case. It would only be false if the field had
141 * been defined NOT NULL (system catalogs could be queried?)
142 *
143 * @param column the first column is 1, the second is 2...
144 * @return one of the columnNullable values
145 * @exception SQLException if a database access error occurs
146 */
147 public int isNullable(int column) throws SQLException
148 {
149 return columnNullable; // We can always put NULL in
150 }
151
152 /**
153 * Is the column a signed number? In PostgreSQL, all numbers
154 * are signed, so this is trivial. However, strings are not
155 * signed (duh!)
156 *
157 * @param column the first column is 1, the second is 2...
158 * @return true if so
159 * @exception SQLException if a database access error occurs
160 */
161 public boolean isSigned(int column) throws SQLException
162 {
163 int sql_type = getField(column).getSQLType();
164
165 switch (sql_type)
166 {
167 case Types.SMALLINT:
168 case Types.INTEGER:
169 case Types.FLOAT:
170 case Types.REAL:
171 case Types.DOUBLE:
172 return true;
173 case Types.DATE:
174 case Types.TIME:
175 case Types.TIMESTAMP:
176 return false; // I don't know about these?
177 default:
178 return false;
179 }
180 }
181
182 /**
183 * What is the column's normal maximum width in characters?
184 *
185 * @param column the first column is 1, the second is 2, etc.
186 * @return the maximum width
187 * @exception SQLException if a database access error occurs
188 */
189 public int getColumnDisplaySize(int column) throws SQLException
190 {
191 Field f = getField(column);
192 String type_name = f.getTypeName();
193 int sql_type = f.getSQLType();
194 int typmod = f.mod;
195
196 // I looked at other JDBC implementations and couldn't find a consistent
197 // interpretation of the "display size" for numeric values, so this is our's
198 // FIXME: currently, only types with a SQL92 or SQL3 pendant are implemented - jens@jens.de
199
200 // fixed length data types
201 if (type_name.equals( "int2" )) return 6; // -32768 to +32768 (5 digits and a sign)
202 if (type_name.equals( "int4" )
203 || type_name.equals( "oid" )) return 11; // -2147483648 to +2147483647
204 if (type_name.equals( "int8" )) return 20; // -9223372036854775808 to +9223372036854775807
205 if (type_name.equals( "money" )) return 12; // MONEY = DECIMAL(9,2)
206 if (type_name.equals( "float4" )) return 11; // i checked it out ans wasn't able to produce more than 11 digits
207 if (type_name.equals( "float8" )) return 20; // dito, 20
208 if (type_name.equals( "char" )) return 1;
209 if (type_name.equals( "bool" )) return 1;
210 if (type_name.equals( "date" )) return 14; // "01/01/4713 BC" - "31/12/32767 AD"
211 if (type_name.equals( "time" )) return 8; // 00:00:00-23:59:59
212 if (type_name.equals( "timestamp" )) return 22; // hhmmm ... the output looks like this: 1999-08-03 22:22:08+02
213
214 // variable length fields
215 typmod -= 4;
216 if (type_name.equals( "bpchar" )
217 || type_name.equals( "varchar" )) return typmod; // VARHDRSZ=sizeof(int32)=4
218 if (type_name.equals( "numeric" )) return ( (typmod >>16) & 0xffff )
219 + 1 + ( typmod & 0xffff ); // DECIMAL(p,s) = (p digits).(s digits)
220
221 // if we don't know better
222 return f.length;
223 }
224
225 /**
226 * What is the suggested column title for use in printouts and
227 * displays? We suggest the ColumnName!
228 *
229 * @param column the first column is 1, the second is 2, etc.
230 * @return the column label
231 * @exception SQLException if a database access error occurs
232 */
233 public String getColumnLabel(int column) throws SQLException
234 {
235 return getColumnName(column);
236 }
237
238 /**
239 * What's a column's name?
240 *
241 * @param column the first column is 1, the second is 2, etc.
242 * @return the column name
243 * @exception SQLException if a database access error occurs
244 */
245 public String getColumnName(int column) throws SQLException
246 {
247 Field f = getField(column);
248 if(f!=null)
249 return f.name;
250 return "field"+column;
251 }
252
253 /**
254 * What is a column's table's schema? This relies on us knowing
255 * the table name....which I don't know how to do as yet. The
256 * JDBC specification allows us to return "" if this is not
257 * applicable.
258 *
259 * @param column the first column is 1, the second is 2...
260 * @return the Schema
261 * @exception SQLException if a database access error occurs
262 */
263 public String getSchemaName(int column) throws SQLException
264 {
265 return "";
266 }
267
268 /**
269 * What is a column's number of decimal digits.
270 *
271 * @param column the first column is 1, the second is 2...
272 * @return the precision
273 * @exception SQLException if a database access error occurs
274 */
275 public int getPrecision(int column) throws SQLException
276 {
277 int sql_type = getField(column).getSQLType();
278
279 switch (sql_type)
280 {
281 case Types.SMALLINT:
282 return 5;
283 case Types.INTEGER:
284 return 10;
285 case Types.REAL:
286 return 8;
287 case Types.FLOAT:
288 return 16;
289 case Types.DOUBLE:
290 return 16;
291 case Types.VARCHAR:
292 return 0;
293 default:
294 return 0;
295 }
296 }
297
298 /**
299 * What is a column's number of digits to the right of the
300 * decimal point?
301 *
302 * @param column the first column is 1, the second is 2...
303 * @return the scale
304 * @exception SQLException if a database access error occurs
305 */
306 public int getScale(int column) throws SQLException
307 {
308 int sql_type = getField(column).getSQLType();
309
310 switch (sql_type)
311 {
312 case Types.SMALLINT:
313 return 0;
314 case Types.INTEGER:
315 return 0;
316 case Types.REAL:
317 return 8;
318 case Types.FLOAT:
319 return 16;
320 case Types.DOUBLE:
321 return 16;
322 case Types.VARCHAR:
323 return 0;
324 default:
325 return 0;
326 }
327 }
328
329 /**
330 * Whats a column's table's name? How do I find this out? Both
331 * getSchemaName() and getCatalogName() rely on knowing the table
332 * Name, so we need this before we can work on them.
333 *
334 * @param column the first column is 1, the second is 2...
335 * @return column name, or "" if not applicable
336 * @exception SQLException if a database access error occurs
337 */
338 public String getTableName(int column) throws SQLException
339 {
340 return "";
341 }
342
343 /**
344 * What's a column's table's catalog name? As with getSchemaName(),
345 * we can say that if getTableName() returns n/a, then we can too -
346 * otherwise, we need to work on it.
347 *
348 * @param column the first column is 1, the second is 2...
349 * @return catalog name, or "" if not applicable
350 * @exception SQLException if a database access error occurs
351 */
352 public String getCatalogName(int column) throws SQLException
353 {
354 return "";
355 }
356
357 /**
358 * What is a column's SQL Type? (java.sql.Type int)
359 *
360 * @param column the first column is 1, the second is 2, etc.
361 * @return the java.sql.Type value
362 * @exception SQLException if a database access error occurs
363 * @see postgresql.Field#getSQLType
364 * @see java.sql.Types
365 */
366 public int getColumnType(int column) throws SQLException
367 {
368 return getField(column).getSQLType();
369 }
370
371 /**
372 * Whats is the column's data source specific type name?
373 *
374 * @param column the first column is 1, the second is 2, etc.
375 * @return the type name
376 * @exception SQLException if a database access error occurs
377 */
378 public String getColumnTypeName(int column) throws SQLException
379 {
380 return getField(column).getTypeName();
381 }
382
383 /**
384 * Is the column definitely not writable? In reality, we would
385 * have to check the GRANT/REVOKE stuff for this to be effective,
386 * and I haven't really looked into that yet, so this will get
387 * re-visited.
388 *
389 * @param column the first column is 1, the second is 2, etc.
390 * @return true if so
391 * @exception SQLException if a database access error occurs
392 */
393 public boolean isReadOnly(int column) throws SQLException
394 {
395 return false;
396 }
397
398 /**
399 * Is it possible for a write on the column to succeed? Again, we
400 * would in reality have to check the GRANT/REVOKE stuff, which
401 * I haven't worked with as yet. However, if it isn't ReadOnly, then
402 * it is obviously writable.
403 *
404 * @param column the first column is 1, the second is 2, etc.
405 * @return true if so
406 * @exception SQLException if a database access error occurs
407 */
408 public boolean isWritable(int column) throws SQLException
409 {
410 if (isReadOnly(column))
411 return true;
412 else
413 return false;
414 }
415
416 /**
417 * Will a write on this column definately succeed? Hmmm...this
418 * is a bad one, since the two preceding functions have not been
419 * really defined. I cannot tell is the short answer. I thus
420 * return isWritable() just to give us an idea.
421 *
422 * @param column the first column is 1, the second is 2, etc..
423 * @return true if so
424 * @exception SQLException if a database access error occurs
425 */
426 public boolean isDefinitelyWritable(int column) throws SQLException
427 {
428 return isWritable(column);
429 }
430
431 // ********************************************************
432 // END OF PUBLIC INTERFACE
433 // ********************************************************
434
435 /**
436 * For several routines in this package, we need to convert
437 * a columnIndex into a Field[] descriptor. Rather than do
438 * the same code several times, here it is.
439 *
440 * @param columnIndex the first column is 1, the second is 2...
441 * @return the Field description
442 * @exception SQLException if a database access error occurs
443 */
444 private Field getField(int columnIndex) throws SQLException
445 {
446 if (columnIndex < 1 || columnIndex > fields.length)
447 throw new PSQLException("postgresql.res.colrange");
448 return fields[columnIndex - 1];
449 }
450 }
451