]> granicus.if.org Git - postgresql/blob - src/interfaces/jdbc/org/postgresql/jdbc1/DatabaseMetaData.java
Removed the 8k row limit reported by DatabaseMetaData
[postgresql] / src / interfaces / jdbc / org / postgresql / jdbc1 / DatabaseMetaData.java
1 package org.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 // org.postgresql.jdbc2 package.
7
8 import java.sql.*;
9 import java.util.*;
10 import org.postgresql.Field;
11
12 /**
13  * This class provides information about the database as a whole.
14  *
15  * <p>Many of the methods here return lists of information in ResultSets.  You
16  * can use the normal ResultSet methods such as getString and getInt to 
17  * retrieve the data from these ResultSets.  If a given form of metadata is
18  * not available, these methods should throw a SQLException.
19  *
20  * <p>Some of these methods take arguments that are String patterns.  These
21  * arguments all have names such as fooPattern.  Within a pattern String,
22  * "%" means match any substring of 0 or more characters, and "_" means
23  * match any one character.  Only metadata entries matching the search
24  * pattern are returned.  if a search pattern argument is set to a null
25  * ref, it means that argument's criteria should be dropped from the
26  * search.
27  *
28  * <p>A SQLException will be throws if a driver does not support a meta
29  * data method.  In the case of methods that return a ResultSet, either
30  * a ResultSet (which may be empty) is returned or a SQLException is
31  * thrown.
32  *
33  * @see java.sql.DatabaseMetaData
34  */
35 public class DatabaseMetaData implements java.sql.DatabaseMetaData 
36 {
37   Connection connection;                // The connection association
38   
39   // These define various OID's. Hopefully they will stay constant.
40   static final int iVarcharOid = 1043;  // OID for varchar
41   static final int iBoolOid = 16;       // OID for bool
42   static final int iInt2Oid = 21;       // OID for int2
43   static final int iInt4Oid = 23;       // OID for int4
44   static final int VARHDRSZ =  4;       // length for int4
45   
46   // This is a default value for remarks
47   private static final byte defaultRemarks[]="no remarks".getBytes();
48   
49   public DatabaseMetaData(Connection conn)
50   {
51     this.connection = conn;
52   }
53   
54   /**
55    * Can all the procedures returned by getProcedures be called
56    * by the current user?
57    *
58    * @return true if so
59    * @exception SQLException if a database access error occurs
60    */
61   public boolean allProceduresAreCallable() throws SQLException
62   {
63     return true;                // For now...
64   }
65   
66   /**
67    * Can all the tables returned by getTable be SELECTed by
68    * the current user?
69    *
70    * @return true if so
71    * @exception SQLException if a database access error occurs
72    */
73   public boolean allTablesAreSelectable() throws SQLException
74   {
75     return true;                // For now...
76   }
77   
78   /**
79    * What is the URL for this database?
80    *
81    * @return the url or null if it cannott be generated
82    * @exception SQLException if a database access error occurs
83    */
84   public String getURL() throws SQLException
85   {
86     return connection.getURL();
87   }
88   
89   /**
90    * What is our user name as known to the database?
91    *
92    * @return our database user name
93    * @exception SQLException if a database access error occurs
94    */
95   public String getUserName() throws SQLException
96   {
97     return connection.getUserName();
98   }
99   
100   /**
101    * Is the database in read-only mode?
102    *
103    * @return true if so
104    * @exception SQLException if a database access error occurs
105    */
106   public boolean isReadOnly() throws SQLException
107   {
108     return connection.isReadOnly();
109   }
110   
111   /**
112    * Are NULL values sorted high?
113    *
114    * @return true if so
115    * @exception SQLException if a database access error occurs
116    */
117   public boolean nullsAreSortedHigh() throws SQLException
118   {
119     return false;
120   }
121   
122   /**
123    * Are NULL values sorted low?
124    *
125    * @return true if so
126    * @exception SQLException if a database access error occurs
127    */
128   public boolean nullsAreSortedLow() throws SQLException
129   {
130     return false;
131   }
132   
133   /**
134    * Are NULL values sorted at the start regardless of sort order?
135    *
136    * @return true if so
137    * @exception SQLException if a database access error occurs
138    */
139   public boolean nullsAreSortedAtStart() throws SQLException
140   {
141     return false;
142   }
143   
144   /**
145    * Are NULL values sorted at the end regardless of sort order?
146    *
147    * @return true if so
148    * @exception SQLException if a database access error occurs
149    */
150   public boolean nullsAreSortedAtEnd() throws SQLException
151   {
152     return true;
153   }
154   
155   /**
156    * What is the name of this database product - we hope that it is
157    * PostgreSQL, so we return that explicitly.
158    *
159    * @return the database product name
160    * @exception SQLException if a database access error occurs
161    */
162   public String getDatabaseProductName() throws SQLException
163   {
164     return "PostgreSQL";
165   }
166   
167   /**
168    * What is the version of this database product.
169    *
170    * @return the database version
171    * @exception SQLException if a database access error occurs
172    */
173   public String getDatabaseProductVersion() throws SQLException
174   {
175         java.sql.ResultSet resultSet = connection.ExecSQL("select version()");
176         resultSet.next();
177
178         StringTokenizer versionParts = new StringTokenizer(resultSet.getString(1));
179         versionParts.nextToken(); /* "PostgreSQL" */
180         String versionNumber = versionParts.nextToken(); /* "X.Y.Z" */
181
182         return versionNumber;
183   }
184   
185   /**
186    * What is the name of this JDBC driver?  If we don't know this
187    * we are doing something wrong!
188    *
189    * @return the JDBC driver name
190    * @exception SQLException why?
191    */
192   public String getDriverName() throws SQLException
193   {
194     return "PostgreSQL Native Driver";
195   }
196   
197   /**
198    * What is the version string of this JDBC driver?  Again, this is
199    * static.
200    *
201    * @return the JDBC driver name.
202    * @exception SQLException why?
203    */
204   public String getDriverVersion() throws SQLException
205   {
206       return connection.this_driver.getVersion();
207   }
208   
209   /**
210    * What is this JDBC driver's major version number?
211    *
212    * @return the JDBC driver major version
213    */
214   public int getDriverMajorVersion()
215   {
216     return connection.this_driver.getMajorVersion();
217   }
218   
219   /**
220    * What is this JDBC driver's minor version number?
221    *
222    * @return the JDBC driver minor version
223    */
224   public int getDriverMinorVersion()
225   {
226     return connection.this_driver.getMinorVersion();
227   }
228   
229   /**
230    * Does the database store tables in a local file?  No - it
231    * stores them in a file on the server.
232    * 
233    * @return true if so
234    * @exception SQLException if a database access error occurs
235    */
236   public boolean usesLocalFiles() throws SQLException
237   {
238     return false;
239   }
240   
241   /**
242    * Does the database use a file for each table?  Well, not really,
243    * since it doesnt use local files. 
244    *
245    * @return true if so
246    * @exception SQLException if a database access error occurs
247    */
248   public boolean usesLocalFilePerTable() throws SQLException
249   {
250     return false;
251   }
252   
253   /**
254    * Does the database treat mixed case unquoted SQL identifiers
255    * as case sensitive and as a result store them in mixed case?
256    * A JDBC-Compliant driver will always return false.
257    *
258    * <p>Predicament - what do they mean by "SQL identifiers" - if it
259    * means the names of the tables and columns, then the answers
260    * given below are correct - otherwise I don't know.
261    *
262    * @return true if so
263    * @exception SQLException if a database access error occurs
264    */
265   public boolean supportsMixedCaseIdentifiers() throws SQLException
266   {
267     return false;
268   }
269   
270   /**
271    * Does the database treat mixed case unquoted SQL identifiers as
272    * case insensitive and store them in upper case?
273    *
274    * @return true if so
275    */
276   public boolean storesUpperCaseIdentifiers() throws SQLException
277   {
278     return false;
279   }
280   
281   /**
282    * Does the database treat mixed case unquoted SQL identifiers as
283    * case insensitive and store them in lower case?
284    *
285    * @return true if so
286    */
287   public boolean storesLowerCaseIdentifiers() throws SQLException
288   {
289     return true;
290   }
291   
292   /**
293    * Does the database treat mixed case unquoted SQL identifiers as
294    * case insensitive and store them in mixed case?
295    *
296    * @return true if so
297    */
298   public boolean storesMixedCaseIdentifiers() throws SQLException
299   {
300     return false;
301   }
302   
303   /**
304    * Does the database treat mixed case quoted SQL identifiers as
305    * case sensitive and as a result store them in mixed case?  A
306    * JDBC compliant driver will always return true. 
307    *
308    * <p>Predicament - what do they mean by "SQL identifiers" - if it
309    * means the names of the tables and columns, then the answers
310    * given below are correct - otherwise I don't know.
311    *
312    * @return true if so
313    * @exception SQLException if a database access error occurs
314    */
315   public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException
316   {
317     return true;
318   }
319   
320   /**
321    * Does the database treat mixed case quoted SQL identifiers as
322    * case insensitive and store them in upper case?
323    *
324    * @return true if so
325    */
326   public boolean storesUpperCaseQuotedIdentifiers() throws SQLException
327   {
328     return false;
329   }
330   
331   /**
332    * Does the database treat mixed case quoted SQL identifiers as case
333    * insensitive and store them in lower case?
334    *
335    * @return true if so
336    */
337   public boolean storesLowerCaseQuotedIdentifiers() throws SQLException
338   {
339     return false;
340   }
341   
342   /**
343    * Does the database treat mixed case quoted SQL identifiers as case
344    * insensitive and store them in mixed case?
345    *
346    * @return true if so
347    */
348   public boolean storesMixedCaseQuotedIdentifiers() throws SQLException
349   {
350     return false;
351   }
352   
353   /**
354    * What is the string used to quote SQL identifiers?  This returns
355    * a space if identifier quoting isn't supported.  A JDBC Compliant
356    * driver will always use a double quote character.
357    *
358    * <p>If an SQL identifier is a table name, column name, etc. then
359    * we do not support it.
360    *
361    * @return the quoting string
362    * @exception SQLException if a database access error occurs
363    */
364   public String getIdentifierQuoteString() throws SQLException
365   {
366     return "\"";
367   }
368   
369   /**
370    * Get a comma separated list of all a database's SQL keywords that
371    * are NOT also SQL92 keywords.
372    *
373    * <p>Within PostgreSQL, the keywords are found in
374    *    src/backend/parser/keywords.c
375    *
376    * <p>For SQL Keywords, I took the list provided at
377    *    <a href="http://web.dementia.org/~shadow/sql/sql3bnf.sep93.txt">
378    * http://web.dementia.org/~shadow/sql/sql3bnf.sep93.txt</a>
379    * which is for SQL3, not SQL-92, but it is close enough for
380    * this purpose.
381    *
382    * @return a comma separated list of keywords we use
383    * @exception SQLException if a database access error occurs
384    */
385   public String getSQLKeywords() throws SQLException
386   {
387     return "abort,acl,add,aggregate,append,archive,arch_store,backward,binary,change,cluster,copy,database,delimiters,do,extend,explain,forward,heavy,index,inherits,isnull,light,listen,load,merge,nothing,notify,notnull,oids,purge,rename,replace,retrieve,returns,rule,recipe,setof,stdin,stdout,store,vacuum,verbose,version";
388   }
389   
390   public String getNumericFunctions() throws SQLException
391   {
392     // XXX-Not Implemented
393     return "";
394   }
395   
396   public String getStringFunctions() throws SQLException
397   {
398     // XXX-Not Implemented
399     return "";
400   }
401   
402   public String getSystemFunctions() throws SQLException
403   {
404     // XXX-Not Implemented
405     return "";
406   }
407   
408   public String getTimeDateFunctions() throws SQLException
409   {
410     // XXX-Not Implemented
411     return "";
412   }
413   
414   /**
415    * This is the string that can be used to escape '_' and '%' in
416    * a search string pattern style catalog search parameters
417    *
418    * @return the string used to escape wildcard characters
419    * @exception SQLException if a database access error occurs
420    */
421   public String getSearchStringEscape() throws SQLException
422   {
423     return "\\";
424   }
425   
426   /**
427    * Get all the "extra" characters that can bew used in unquoted
428    * identifier names (those beyond a-zA-Z0-9 and _)
429    *
430    * <p>From the file src/backend/parser/scan.l, an identifier is
431    * {letter}{letter_or_digit} which makes it just those listed
432    * above.
433    *
434    * @return a string containing the extra characters
435    * @exception SQLException if a database access error occurs
436    */
437   public String getExtraNameCharacters() throws SQLException
438   {
439     return "";
440   }
441   
442   /**
443    * Is "ALTER TABLE" with an add column supported?
444    * Yes for PostgreSQL 6.1
445    *
446    * @return true if so
447    * @exception SQLException if a database access error occurs
448    */
449   public boolean supportsAlterTableWithAddColumn() throws SQLException
450   {
451     return true;
452   }
453   
454   /**
455    * Is "ALTER TABLE" with a drop column supported?
456    * Peter 10/10/2000 This was set to true, but 7.1devel doesn't support it!
457    *
458    * @return true if so
459    * @exception SQLException if a database access error occurs
460    */
461   public boolean supportsAlterTableWithDropColumn() throws SQLException
462   {
463       return false;
464   }
465   
466   /**
467    * Is column aliasing supported?
468    *
469    * <p>If so, the SQL AS clause can be used to provide names for
470    * computed columns or to provide alias names for columns as
471    * required.  A JDBC Compliant driver always returns true.
472    *
473    * <p>e.g.
474    *
475    * <br><pre>
476    * select count(C) as C_COUNT from T group by C;
477    *
478    * </pre><br>
479    * should return a column named as C_COUNT instead of count(C)
480    *
481    * @return true if so
482    * @exception SQLException if a database access error occurs
483    */
484   public boolean supportsColumnAliasing() throws SQLException
485   {
486     return true;
487   }
488   
489   /**
490    * Are concatenations between NULL and non-NULL values NULL?  A
491    * JDBC Compliant driver always returns true
492    *
493    * @return true if so
494    * @exception SQLException if a database access error occurs
495    */
496   public boolean nullPlusNonNullIsNull() throws SQLException
497   {
498     return true;
499   }
500   
501   public boolean supportsConvert() throws SQLException
502   {
503     // XXX-Not Implemented
504     return false;
505   }
506   
507   public boolean supportsConvert(int fromType, int toType) throws SQLException
508   {
509     // XXX-Not Implemented
510     return false;
511   }
512   
513   public boolean supportsTableCorrelationNames() throws SQLException
514   {
515     // XXX-Not Implemented
516     return false;
517   }
518   
519   public boolean supportsDifferentTableCorrelationNames() throws SQLException
520   {
521     // XXX-Not Implemented
522     return false;
523   }
524   
525   /**
526    * Are expressions in "ORCER BY" lists supported?
527    * 
528    * <br>e.g. select * from t order by a + b;
529    *
530    * @return true if so
531    * @exception SQLException if a database access error occurs
532    */
533   public boolean supportsExpressionsInOrderBy() throws SQLException
534   {
535     return true;
536   }
537   
538   /**
539    * Can an "ORDER BY" clause use columns not in the SELECT?
540    * I checked it, and you can't.
541    *
542    * @return true if so
543    * @exception SQLException if a database access error occurs
544    */
545   public boolean supportsOrderByUnrelated() throws SQLException
546   {
547     return false;
548   }
549   
550   /**
551    * Is some form of "GROUP BY" clause supported?
552    * I checked it, and yes it is.
553    *
554    * @return true if so
555    * @exception SQLException if a database access error occurs
556    */
557   public boolean supportsGroupBy() throws SQLException
558   {
559     return true;
560   }
561   
562   /**
563    * Can a "GROUP BY" clause use columns not in the SELECT?
564    * I checked it - it seems to allow it
565    *
566    * @return true if so
567    * @exception SQLException if a database access error occurs
568    */
569   public boolean supportsGroupByUnrelated() throws SQLException
570   {
571     return true;
572   }
573   
574   /**
575    * Can a "GROUP BY" clause add columns not in the SELECT provided
576    * it specifies all the columns in the SELECT?  Does anyone actually
577    * understand what they mean here?
578    *
579    * @return true if so
580    * @exception SQLException if a database access error occurs
581    */
582   public boolean supportsGroupByBeyondSelect() throws SQLException
583   {
584     return true;                // For now...
585   }
586   
587   /**
588    * Is the escape character in "LIKE" clauses supported?  A
589    * JDBC compliant driver always returns true.
590    *
591    * @return true if so
592    * @exception SQLException if a database access error occurs
593    */
594   public boolean supportsLikeEscapeClause() throws SQLException
595   {
596     return true;
597   }
598   
599   /**
600    * Are multiple ResultSets from a single execute supported?
601    * Well, I implemented it, but I dont think this is possible from
602    * the back ends point of view.
603    * 
604    * @return true if so
605    * @exception SQLException if a database access error occurs
606    */
607   public boolean supportsMultipleResultSets() throws SQLException
608   {
609     return false;
610   }
611   
612   /**
613    * Can we have multiple transactions open at once (on different
614    * connections?)
615    * I guess we can have, since Im relying on it.
616    *
617    * @return true if so
618    * @exception SQLException if a database access error occurs
619    */
620   public boolean supportsMultipleTransactions() throws SQLException
621   {
622     return true;
623   }
624   
625   /**
626    * Can columns be defined as non-nullable.  A JDBC Compliant driver
627    * always returns true.
628    *
629    * <p>This changed from false to true in v6.2 of the driver, as this
630    * support was added to the backend.
631    *
632    * @return true if so
633    * @exception SQLException if a database access error occurs
634    */
635   public boolean supportsNonNullableColumns() throws SQLException
636   {
637     return true;
638   }
639   
640   /**
641    * Does this driver support the minimum ODBC SQL grammar.  This
642    * grammar is defined at:
643    *
644    * <p><a href="http://www.microsoft.com/msdn/sdk/platforms/doc/odbc/src/intropr.htm">http://www.microsoft.com/msdn/sdk/platforms/doc/odbc/src/intropr.htm</a>
645    *
646    * <p>In Appendix C.  From this description, we seem to support the
647    * ODBC minimal (Level 0) grammar.
648    *
649    * @return true if so
650    * @exception SQLException if a database access error occurs
651    */
652   public boolean supportsMinimumSQLGrammar() throws SQLException
653   {
654     return true;
655   }
656   
657   /**
658    * Does this driver support the Core ODBC SQL grammar.  We need
659    * SQL-92 conformance for this.
660    *
661    * @return true if so
662    * @exception SQLException if a database access error occurs
663    */
664   public boolean supportsCoreSQLGrammar() throws SQLException
665   {
666     return false;
667   }
668   
669   /**
670    * Does this driver support the Extended (Level 2) ODBC SQL
671    * grammar.  We don't conform to the Core (Level 1), so we can't
672    * conform to the Extended SQL Grammar.
673    *
674    * @return true if so
675    * @exception SQLException if a database access error occurs
676    */
677   public boolean supportsExtendedSQLGrammar() throws SQLException
678   {
679     return false;
680   }
681   
682   /**
683    * Does this driver support the ANSI-92 entry level SQL grammar?
684    * All JDBC Compliant drivers must return true.  I think we have
685    * to support outer joins for this to be true.
686    *
687    * @return true if so
688    * @exception SQLException if a database access error occurs
689    */
690   public boolean supportsANSI92EntryLevelSQL() throws SQLException
691   {
692     return false;
693   }
694   
695   /**
696    * Does this driver support the ANSI-92 intermediate level SQL
697    * grammar?  Anyone who does not support Entry level cannot support
698    * Intermediate level.
699    *
700    * @return true if so
701    * @exception SQLException if a database access error occurs
702    */
703   public boolean supportsANSI92IntermediateSQL() throws SQLException
704   {
705     return false;
706   }
707   
708   /**
709    * Does this driver support the ANSI-92 full SQL grammar?
710    *
711    * @return true if so
712    * @exception SQLException if a database access error occurs
713    */
714   public boolean supportsANSI92FullSQL() throws SQLException
715   {
716     return false;
717   }
718   
719   /**
720    * Is the SQL Integrity Enhancement Facility supported?
721    * I haven't seen this mentioned anywhere, so I guess not
722    * 
723    * @return true if so
724    * @exception SQLException if a database access error occurs
725    */
726   public boolean supportsIntegrityEnhancementFacility() throws SQLException
727   {
728     return false;
729   }
730   
731   /**
732    * Is some form of outer join supported?  From my knowledge, nope.
733    *
734    * @return true if so
735    * @exception SQLException if a database access error occurs
736    */
737   public boolean supportsOuterJoins() throws SQLException
738   {
739     return false;
740   }
741   
742   /**
743    * Are full nexted outer joins supported?  Well, we dont support any
744    * form of outer join, so this is no as well
745    *
746    * @return true if so
747    * @exception SQLException if a database access error occurs
748    */
749   public boolean supportsFullOuterJoins() throws SQLException
750   {
751     return false;
752   }
753   
754   /**
755    * Is there limited support for outer joins?  (This will be true if
756    * supportFullOuterJoins is true)
757    *
758    * @return true if so
759    * @exception SQLException if a database access error occurs
760    */
761   public boolean supportsLimitedOuterJoins() throws SQLException
762   {
763     return false;
764   }
765   
766   /**
767    * What is the database vendor's preferred term for "schema" - well,
768    * we do not provide support for schemas, so lets just use that
769    * term.
770    *
771    * @return the vendor term
772    * @exception SQLException if a database access error occurs
773    */
774   public String getSchemaTerm() throws SQLException
775   {
776     return "Schema";
777   }
778   
779   /**
780    * What is the database vendor's preferred term for "procedure" - 
781    * I kind of like "Procedure" myself.
782    *
783    * @return the vendor term
784    * @exception SQLException if a database access error occurs
785    */
786   public String getProcedureTerm() throws SQLException
787   {
788     return "Procedure";
789   }
790   
791   /**
792    * What is the database vendor's preferred term for "catalog"? -
793    * we dont have a preferred term, so just use Catalog
794    *
795    * @return the vendor term
796    * @exception SQLException if a database access error occurs
797    */
798   public String getCatalogTerm() throws SQLException
799   {
800     return "Catalog";
801   }
802   
803   /**
804    * Does a catalog appear at the start of a qualified table name?
805    * (Otherwise it appears at the end).
806    *
807    * @return true if so
808    * @exception SQLException if a database access error occurs
809    */
810   public boolean isCatalogAtStart() throws SQLException
811   {
812     return false;
813   }
814   
815   /**
816    * What is the Catalog separator.  Hmmm....well, I kind of like
817    * a period (so we get catalog.table definitions). - I don't think
818    * PostgreSQL supports catalogs anyhow, so it makes no difference.
819    *
820    * @return the catalog separator string
821    * @exception SQLException if a database access error occurs
822    */
823   public String getCatalogSeparator() throws SQLException
824   {
825     // PM Sep 29 97 - changed from "." as we don't support catalogs.
826     return "";
827   }
828   
829   /**
830    * Can a schema name be used in a data manipulation statement?  Nope.
831    *
832    * @return true if so
833    * @exception SQLException if a database access error occurs
834    */
835   public boolean supportsSchemasInDataManipulation() throws SQLException
836   {
837     return false;
838   }
839   
840   /**
841    * Can a schema name be used in a procedure call statement?  Nope.
842    *
843    * @return true if so
844    * @exception SQLException if a database access error occurs
845    */
846   public boolean supportsSchemasInProcedureCalls() throws SQLException
847   {
848     return false;
849   }
850   
851   /**
852    * Can a schema be used in a table definition statement?  Nope.
853    *
854    * @return true if so
855    * @exception SQLException if a database access error occurs
856    */
857   public boolean supportsSchemasInTableDefinitions() throws SQLException
858   {
859     return false;
860   }
861   
862   /**
863    * Can a schema name be used in an index definition statement?
864    *
865    * @return true if so
866    * @exception SQLException if a database access error occurs
867    */
868   public boolean supportsSchemasInIndexDefinitions() throws SQLException
869   {
870     return false;
871   }
872   
873   /**
874    * Can a schema name be used in a privilege definition statement?
875    *
876    * @return true if so
877    * @exception SQLException if a database access error occurs
878    */
879   public boolean supportsSchemasInPrivilegeDefinitions() throws SQLException
880   {
881     return false;
882   }
883   
884   /**
885    * Can a catalog name be used in a data manipulation statement?
886    *
887    * @return true if so
888    * @exception SQLException if a database access error occurs
889    */
890   public boolean supportsCatalogsInDataManipulation() throws SQLException
891   {
892     return false;
893   }
894   
895   /**
896    * Can a catalog name be used in a procedure call statement?
897    *
898    * @return true if so
899    * @exception SQLException if a database access error occurs
900    */
901   public boolean supportsCatalogsInProcedureCalls() throws SQLException
902   {
903     return false;
904   }
905   
906   /**
907    * Can a catalog name be used in a table definition statement?
908    *
909    * @return true if so
910    * @exception SQLException if a database access error occurs
911    */
912   public boolean supportsCatalogsInTableDefinitions() throws SQLException
913   {
914     return false;
915   }
916   
917   /**
918    * Can a catalog name be used in an index definition?
919    *
920    * @return true if so
921    * @exception SQLException if a database access error occurs
922    */
923   public boolean supportsCatalogsInIndexDefinitions() throws SQLException
924   {
925     return false;
926   }
927   
928   /**
929    * Can a catalog name be used in a privilege definition statement?
930    *
931    * @return true if so
932    * @exception SQLException if a database access error occurs
933    */
934   public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException
935   {
936     return false;
937   }
938   
939   /**
940    * We support cursors for gets only it seems.  I dont see a method
941    * to get a positioned delete.
942    *
943    * @return true if so
944    * @exception SQLException if a database access error occurs
945    */
946   public boolean supportsPositionedDelete() throws SQLException
947   {
948     return false;                       // For now...
949   }
950   
951   /**
952    * Is positioned UPDATE supported?
953    * 
954    * @return true if so
955    * @exception SQLException if a database access error occurs
956    */
957   public boolean supportsPositionedUpdate() throws SQLException
958   {
959     return false;                       // For now...
960   }
961   
962   public boolean supportsSelectForUpdate() throws SQLException
963   {
964     // XXX-Not Implemented
965     return false;
966   }
967   
968   public boolean supportsStoredProcedures() throws SQLException
969   {
970     // XXX-Not Implemented
971     return false;
972   }
973   
974   public boolean supportsSubqueriesInComparisons() throws SQLException
975   {
976     // XXX-Not Implemented
977     return false;
978   }
979   
980   public boolean supportsSubqueriesInExists() throws SQLException
981   {
982     // XXX-Not Implemented
983     return false;
984   }
985   
986   public boolean supportsSubqueriesInIns() throws SQLException
987   {
988     // XXX-Not Implemented
989     return false;
990   }
991   
992   public boolean supportsSubqueriesInQuantifieds() throws SQLException
993   {
994     // XXX-Not Implemented
995     return false;
996   }
997   
998   public boolean supportsCorrelatedSubqueries() throws SQLException
999   {
1000     // XXX-Not Implemented
1001     return false;
1002   }
1003   
1004   /**
1005    * Is SQL UNION supported?  Nope.
1006    *
1007    * @return true if so
1008    * @exception SQLException if a database access error occurs
1009    */
1010   public boolean supportsUnion() throws SQLException
1011   {
1012     return false;
1013   }
1014   
1015   /**
1016    * Is SQL UNION ALL supported?  Nope.
1017    *
1018    * @return true if so
1019    * @exception SQLException if a database access error occurs
1020    */
1021   public boolean supportsUnionAll() throws SQLException
1022   {
1023     return false;
1024   }
1025   
1026   /**
1027    * In PostgreSQL, Cursors are only open within transactions.
1028    *
1029    * @return true if so
1030    * @exception SQLException if a database access error occurs
1031    */
1032   public boolean supportsOpenCursorsAcrossCommit() throws SQLException
1033   {
1034     return false;
1035   }
1036   
1037   /**
1038    * Do we support open cursors across multiple transactions?
1039    *
1040    * @return true if so
1041    * @exception SQLException if a database access error occurs
1042    */
1043   public boolean supportsOpenCursorsAcrossRollback() throws SQLException
1044   {
1045     return false;
1046   }
1047   
1048   /**
1049    * Can statements remain open across commits?  They may, but
1050    * this driver cannot guarentee that.  In further reflection.
1051    * we are talking a Statement object jere, so the answer is
1052    * yes, since the Statement is only a vehicle to ExecSQL()
1053    *
1054    * @return true if they always remain open; false otherwise
1055    * @exception SQLException if a database access error occurs
1056    */
1057   public boolean supportsOpenStatementsAcrossCommit() throws SQLException
1058   {
1059     return true;
1060   }
1061   
1062   /**
1063    * Can statements remain open across rollbacks?  They may, but
1064    * this driver cannot guarentee that.  In further contemplation,
1065    * we are talking a Statement object here, so the answer is yes,
1066    * since the Statement is only a vehicle to ExecSQL() in Connection
1067    *
1068    * @return true if they always remain open; false otherwise
1069    * @exception SQLException if a database access error occurs
1070    */
1071   public boolean supportsOpenStatementsAcrossRollback() throws SQLException
1072   {
1073     return true;
1074   }
1075   
1076   /**
1077    * How many hex characters can you have in an inline binary literal
1078    *
1079    * @return the max literal length
1080    * @exception SQLException if a database access error occurs
1081    */
1082   public int getMaxBinaryLiteralLength() throws SQLException
1083   {
1084     return 0;                           // For now...
1085   }
1086   
1087   /**
1088    * What is the maximum length for a character literal
1089    * I suppose it is 8190 (8192 - 2 for the quotes)
1090    *
1091    * @return the max literal length
1092    * @exception SQLException if a database access error occurs
1093    */
1094   public int getMaxCharLiteralLength() throws SQLException
1095   {
1096     return 65535;
1097   }
1098   
1099   /**
1100    * Whats the limit on column name length.  The description of
1101    * pg_class would say '32' (length of pg_class.relname) - we
1102    * should probably do a query for this....but....
1103    *
1104    * @return the maximum column name length
1105    * @exception SQLException if a database access error occurs
1106    */
1107   public int getMaxColumnNameLength() throws SQLException
1108   {
1109     return 32;
1110   }
1111   
1112   /**
1113    * What is the maximum number of columns in a "GROUP BY" clause?
1114    *
1115    * @return the max number of columns
1116    * @exception SQLException if a database access error occurs  
1117    */
1118   public int getMaxColumnsInGroupBy() throws SQLException
1119   {
1120     return getMaxColumnsInTable();
1121   }
1122   
1123   /**
1124    * What's the maximum number of columns allowed in an index?
1125    * 6.0 only allowed one column, but 6.1 introduced multi-column
1126    * indices, so, theoretically, its all of them.
1127    *
1128    * @return max number of columns
1129    * @exception SQLException if a database access error occurs
1130    */
1131   public int getMaxColumnsInIndex() throws SQLException
1132   {
1133     return getMaxColumnsInTable();
1134   }
1135   
1136   /**
1137    * What's the maximum number of columns in an "ORDER BY clause?
1138    * Theoretically, all of them!
1139    *
1140    * @return the max columns
1141    * @exception SQLException if a database access error occurs
1142    */
1143   public int getMaxColumnsInOrderBy() throws SQLException
1144   {
1145     return getMaxColumnsInTable();
1146   }
1147   
1148   /**
1149    * What is the maximum number of columns in a "SELECT" list?
1150    * Theoretically, all of them!
1151    *
1152    * @return the max columns
1153    * @exception SQLException if a database access error occurs
1154    */
1155   public int getMaxColumnsInSelect() throws SQLException
1156   {
1157     return getMaxColumnsInTable();
1158   }
1159   
1160   /**
1161    * What is the maximum number of columns in a table? From the
1162    * create_table(l) manual page...
1163    *
1164    * <p>"The new class is created as a heap with no initial data.  A
1165    * class can have no more than 1600 attributes (realistically,
1166    * this is limited by the fact that tuple sizes must be less than
1167    * 8192 bytes)..."
1168    *
1169    * @return the max columns
1170    * @exception SQLException if a database access error occurs
1171    */
1172   public int getMaxColumnsInTable() throws SQLException
1173   {
1174     return 1600;
1175   }
1176   
1177   /**
1178    * How many active connection can we have at a time to this
1179    * database?  Well, since it depends on postmaster, which just
1180    * does a listen() followed by an accept() and fork(), its
1181    * basically very high.  Unless the system runs out of processes,
1182    * it can be 65535 (the number of aux. ports on a TCP/IP system).
1183    * I will return 8192 since that is what even the largest system
1184    * can realistically handle,
1185    *
1186    * @return the maximum number of connections
1187    * @exception SQLException if a database access error occurs
1188    */
1189   public int getMaxConnections() throws SQLException
1190   {
1191     return 8192;
1192   }
1193   
1194   /**
1195    * What is the maximum cursor name length (the same as all
1196    * the other F***** identifiers!)
1197    *
1198    * @return max cursor name length in bytes
1199    * @exception SQLException if a database access error occurs
1200    */
1201   public int getMaxCursorNameLength() throws SQLException
1202   {
1203     return 32;
1204   }
1205   
1206   /**
1207    * What is the maximum length of an index (in bytes)?  Now, does
1208    * the spec. mean name of an index (in which case its 32, the 
1209    * same as a table) or does it mean length of an index element
1210    * (in which case its 8192, the size of a row) or does it mean
1211    * the number of rows it can access (in which case it 2^32 - 
1212    * a 4 byte OID number)?  I think its the length of an index
1213    * element, personally, so Im setting it to 65535.
1214    *
1215    * @return max index length in bytes
1216    * @exception SQLException if a database access error occurs
1217    */
1218   public int getMaxIndexLength() throws SQLException
1219   {
1220     return 65535;
1221   }
1222   
1223   public int getMaxSchemaNameLength() throws SQLException
1224   {
1225     // XXX-Not Implemented
1226     return 0;
1227   }
1228   
1229   /**
1230    * What is the maximum length of a procedure name?
1231    * (length of pg_proc.proname used) - again, I really
1232    * should do a query here to get it.
1233    *
1234    * @return the max name length in bytes
1235    * @exception SQLException if a database access error occurs
1236    */
1237   public int getMaxProcedureNameLength() throws SQLException
1238   {
1239     return 32;
1240   }
1241   
1242   public int getMaxCatalogNameLength() throws SQLException
1243   {
1244     // XXX-Not Implemented
1245     return 0;
1246   }
1247   
1248   /**
1249    * What is the maximum length of a single row?  (not including
1250    * blobs).  65535 is defined in PostgreSQL.
1251    *
1252    * @return max row size in bytes
1253    * @exception SQLException if a database access error occurs
1254    */
1255   public int getMaxRowSize() throws SQLException
1256   {
1257     return 65535;
1258   }
1259   
1260   /**
1261    * Did getMaxRowSize() include LONGVARCHAR and LONGVARBINARY
1262    * blobs?  We don't handle blobs yet
1263    *
1264    * @return true if so
1265    * @exception SQLException if a database access error occurs
1266    */
1267   public boolean doesMaxRowSizeIncludeBlobs() throws SQLException
1268   {
1269     return false;
1270   }
1271   
1272   /**
1273    * What is the maximum length of a SQL statement?
1274    *
1275    * @return max length in bytes
1276    * @exception SQLException if a database access error occurs
1277    */
1278   public int getMaxStatementLength() throws SQLException
1279   {
1280     return 65535;
1281   }
1282   
1283   /**
1284    * How many active statements can we have open at one time to
1285    * this database?  Basically, since each Statement downloads
1286    * the results as the query is executed, we can have many.  However,
1287    * we can only really have one statement per connection going
1288    * at once (since they are executed serially) - so we return
1289    * one.
1290    *
1291    * @return the maximum
1292    * @exception SQLException if a database access error occurs
1293    */
1294   public int getMaxStatements() throws SQLException
1295   {
1296     return 1;
1297   }
1298   
1299   /**
1300    * What is the maximum length of a table name?  This was found
1301    * from pg_class.relname length
1302    *
1303    * @return max name length in bytes
1304    * @exception SQLException if a database access error occurs
1305    */
1306   public int getMaxTableNameLength() throws SQLException
1307   {
1308     return 32;
1309   }
1310   
1311   /**
1312    * What is the maximum number of tables that can be specified
1313    * in a SELECT?  Theoretically, this is the same number as the
1314    * number of tables allowable.  In practice tho, it is much smaller
1315    * since the number of tables is limited by the statement, we
1316    * return 1024 here - this is just a number I came up with (being
1317    * the number of tables roughly of three characters each that you
1318    * can fit inside a 65535 character buffer with comma separators).
1319    *
1320    * @return the maximum
1321    * @exception SQLException if a database access error occurs
1322    */
1323   public int getMaxTablesInSelect() throws SQLException
1324   {
1325     return 1024;
1326   }
1327   
1328   /**
1329    * What is the maximum length of a user name?  Well, we generally
1330    * use UNIX like user names in PostgreSQL, so I think this would
1331    * be 8.  However, showing the schema for pg_user shows a length
1332    * for username of 32.
1333    *
1334    * @return the max name length in bytes
1335    * @exception SQLException if a database access error occurs
1336    */
1337   public int getMaxUserNameLength() throws SQLException
1338   {
1339     return 32;
1340   }
1341   
1342   
1343   /**
1344    * What is the database's default transaction isolation level?  We
1345    * do not support this, so all transactions are SERIALIZABLE.
1346    *
1347    * @return the default isolation level
1348    * @exception SQLException if a database access error occurs
1349    * @see Connection
1350    */
1351   public int getDefaultTransactionIsolation() throws SQLException
1352   {
1353     return Connection.TRANSACTION_READ_COMMITTED;
1354   }
1355   
1356   /**
1357    * Are transactions supported?  If not, commit and rollback are noops
1358    * and the isolation level is TRANSACTION_NONE.  We do support
1359    * transactions.      
1360    *
1361    * @return true if transactions are supported
1362    * @exception SQLException if a database access error occurs
1363    */
1364   public boolean supportsTransactions() throws SQLException
1365   {
1366     return true;
1367   }
1368   
1369   /**
1370    * Does the database support the given transaction isolation level?
1371    * We only support TRANSACTION_SERIALIZABLE and TRANSACTION_READ_COMMITTED
1372    * 
1373    * @param level the values are defined in java.sql.Connection
1374    * @return true if so
1375    * @exception SQLException if a database access error occurs
1376    * @see Connection
1377    */
1378   public boolean supportsTransactionIsolationLevel(int level) throws SQLException
1379   {
1380       if (level == Connection.TRANSACTION_SERIALIZABLE ||
1381           level == Connection.TRANSACTION_READ_COMMITTED)
1382           return true;
1383       else
1384           return false;
1385   }
1386   
1387   /**
1388    * Are both data definition and data manipulation transactions 
1389    * supported?  I checked it, and could not do a CREATE TABLE
1390    * within a transaction, so I am assuming that we don't
1391    *
1392    * @return true if so
1393    * @exception SQLException if a database access error occurs
1394    */
1395   public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLException
1396   {
1397     return false;
1398   }
1399   
1400   /**
1401    * Are only data manipulation statements withing a transaction
1402    * supported?
1403    *
1404    * @return true if so
1405    * @exception SQLException if a database access error occurs
1406    */
1407   public boolean supportsDataManipulationTransactionsOnly() throws SQLException
1408   {
1409     return true;
1410   }
1411   
1412   /**
1413    * Does a data definition statement within a transaction force
1414    * the transaction to commit?  I think this means something like:
1415    *
1416    * <p><pre>
1417    * CREATE TABLE T (A INT);
1418    * INSERT INTO T (A) VALUES (2);
1419    * BEGIN;
1420    * UPDATE T SET A = A + 1;
1421    * CREATE TABLE X (A INT);
1422    * SELECT A FROM T INTO X;
1423    * COMMIT;
1424    * </pre><p>
1425    *
1426    * does the CREATE TABLE call cause a commit?  The answer is no.  
1427    *
1428    * @return true if so
1429    * @exception SQLException if a database access error occurs
1430    */
1431   public boolean dataDefinitionCausesTransactionCommit() throws SQLException
1432   {
1433     return false;
1434   }
1435   
1436   /**
1437    * Is a data definition statement within a transaction ignored?
1438    * It seems to be (from experiment in previous method)
1439    *
1440    * @return true if so
1441    * @exception SQLException if a database access error occurs
1442    */
1443   public boolean dataDefinitionIgnoredInTransactions() throws SQLException
1444   {
1445     return true;
1446   }
1447   
1448   /**
1449    * Get a description of stored procedures available in a catalog
1450    * 
1451    * <p>Only procedure descriptions matching the schema and procedure
1452    * name criteria are returned.  They are ordered by PROCEDURE_SCHEM
1453    * and PROCEDURE_NAME
1454    *
1455    * <p>Each procedure description has the following columns:
1456    * <ol>
1457    * <li><b>PROCEDURE_CAT</b> String => procedure catalog (may be null)
1458    * <li><b>PROCEDURE_SCHEM</b> String => procedure schema (may be null)
1459    * <li><b>PROCEDURE_NAME</b> String => procedure name
1460    * <li><b>Field 4</b> reserved (make it null)
1461    * <li><b>Field 5</b> reserved (make it null)
1462    * <li><b>Field 6</b> reserved (make it null)
1463    * <li><b>REMARKS</b> String => explanatory comment on the procedure
1464    * <li><b>PROCEDURE_TYPE</b> short => kind of procedure
1465    *    <ul>
1466    *    <li> procedureResultUnknown - May return a result
1467    *    <li> procedureNoResult - Does not return a result
1468    *    <li> procedureReturnsResult - Returns a result
1469    *    </ul>
1470    * </ol>
1471    *
1472    * @param catalog - a catalog name; "" retrieves those without a
1473    *    catalog; null means drop catalog name from criteria
1474    * @param schemaParrern - a schema name pattern; "" retrieves those
1475    *    without a schema - we ignore this parameter
1476    * @param procedureNamePattern - a procedure name pattern
1477    * @return ResultSet - each row is a procedure description
1478    * @exception SQLException if a database access error occurs
1479    */
1480   public java.sql.ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException
1481   {
1482     // the field descriptors for the new ResultSet
1483     Field f[] = new Field[8];
1484     java.sql.ResultSet r;       // ResultSet for the SQL query that we need to do
1485     Vector v = new Vector();            // The new ResultSet tuple stuff
1486     
1487     byte remarks[] = defaultRemarks;
1488     
1489     f[0] = new Field(connection, "PROCEDURE_CAT",   iVarcharOid, 32);
1490     f[1] = new Field(connection, "PROCEDURE_SCHEM", iVarcharOid, 32);
1491     f[2] = new Field(connection, "PROCEDURE_NAME",  iVarcharOid, 32);
1492     f[3] = f[4] = f[5] = null;  // reserved, must be null for now
1493     f[6] = new Field(connection, "REMARKS",        iVarcharOid, 8192);
1494     f[7] = new Field(connection, "PROCEDURE_TYPE", iInt2Oid,    2);
1495     
1496     // If the pattern is null, then set it to the default
1497     if(procedureNamePattern==null)
1498       procedureNamePattern="%";
1499     
1500     r = connection.ExecSQL("select proname, proretset from pg_proc where proname like '"+procedureNamePattern.toLowerCase()+"' order by proname");
1501     
1502     while (r.next())
1503       {
1504         byte[][] tuple = new byte[8][0];
1505         
1506         tuple[0] = null;                        // Catalog name
1507         tuple[1] = null;                        // Schema name
1508         tuple[2] = r.getBytes(1);               // Procedure name
1509         tuple[3] = tuple[4] = tuple[5] = null;  // Reserved
1510         tuple[6] = remarks;                     // Remarks
1511         
1512         if (r.getBoolean(2))
1513           tuple[7] = Integer.toString(java.sql.DatabaseMetaData.procedureReturnsResult).getBytes();
1514         else
1515           tuple[7] = Integer.toString(java.sql.DatabaseMetaData.procedureNoResult).getBytes();
1516         
1517         v.addElement(tuple);
1518       }
1519     return new ResultSet(connection, f, v, "OK", 1);
1520   }
1521   
1522   /**
1523    * Get a description of a catalog's stored procedure parameters
1524    * and result columns.
1525    *
1526    * <p>Only descriptions matching the schema, procedure and parameter
1527    * name criteria are returned. They are ordered by PROCEDURE_SCHEM
1528    * and PROCEDURE_NAME. Within this, the return value, if any, is
1529    * first. Next are the parameter descriptions in call order. The
1530    * column descriptions follow in column number order.
1531    *
1532    * <p>Each row in the ResultSet is a parameter description or column 
1533    * description with the following fields:
1534    * <ol>
1535    * <li><b>PROCEDURE_CAT</b> String => procedure catalog (may be null)
1536    * <li><b>PROCEDURE_SCHE</b>M String => procedure schema (may be null)
1537    * <li><b>PROCEDURE_NAME</b> String => procedure name
1538    * <li><b>COLUMN_NAME</b> String => column/parameter name
1539    * <li><b>COLUMN_TYPE</b> Short => kind of column/parameter:
1540    * <ul><li>procedureColumnUnknown - nobody knows
1541    * <li>procedureColumnIn - IN parameter
1542    * <li>procedureColumnInOut - INOUT parameter
1543    * <li>procedureColumnOut - OUT parameter
1544    * <li>procedureColumnReturn - procedure return value
1545    * <li>procedureColumnResult - result column in ResultSet
1546    * </ul>
1547    * <li><b>DATA_TYPE</b> short => SQL type from java.sql.Types
1548    * <li><b>TYPE_NAME</b> String => SQL type name
1549    * <li><b>PRECISION</b> int => precision
1550    * <li><b>LENGTH</b> int => length in bytes of data
1551    * <li><b>SCALE</b> short => scale
1552    * <li><b>RADIX</b> short => radix
1553    * <li><b>NULLABLE</b> short => can it contain NULL?
1554    * <ul><li>procedureNoNulls - does not allow NULL values
1555    * <li>procedureNullable - allows NULL values
1556    * <li>procedureNullableUnknown - nullability unknown
1557    * <li><b>REMARKS</b> String => comment describing parameter/column
1558    * </ol>
1559    * @param catalog This is ignored in org.postgresql, advise this is set to null
1560    * @param schemaPattern This is ignored in org.postgresql, advise this is set to null
1561    * @param procedureNamePattern a procedure name pattern
1562    * @param columnNamePattern a column name pattern
1563    * @return each row is a stored procedure parameter or column description
1564    * @exception SQLException if a database-access error occurs
1565    * @see #getSearchStringEscape
1566    */
1567   // Implementation note: This is required for Borland's JBuilder to work
1568   public java.sql.ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) throws SQLException
1569   {
1570     if(procedureNamePattern==null)
1571       procedureNamePattern="%";
1572     
1573     if(columnNamePattern==null)
1574       columnNamePattern="%";
1575     
1576     // for now, this returns an empty result set.
1577     Field f[] = new Field[13];
1578     ResultSet r;        // ResultSet for the SQL query that we need to do
1579     Vector v = new Vector();            // The new ResultSet tuple stuff
1580     
1581     f[0] = new Field(connection, "PROCEDURE_CAT", iVarcharOid, 32);
1582     f[1] = new Field(connection, "PROCEDURE_SCHEM", iVarcharOid, 32);
1583     f[2] = new Field(connection, "PROCEDURE_NAME", iVarcharOid, 32);
1584     f[3] = new Field(connection, "COLUMN_NAME", iVarcharOid, 32);
1585     f[4] = new Field(connection, "COLUMN_TYPE", iInt2Oid, 2);
1586     f[5] = new Field(connection, "DATA_TYPE", iInt2Oid, 2);
1587     f[6] = new Field(connection, "TYPE_NAME", iVarcharOid, 32);
1588     f[7] = new Field(connection, "PRECISION", iInt4Oid, 4);
1589     f[8] = new Field(connection, "LENGTH", iInt4Oid, 4);
1590     f[9] = new Field(connection, "SCALE", iInt2Oid, 2);
1591     f[10] = new Field(connection, "RADIX", iInt2Oid, 2);
1592     f[11] = new Field(connection, "NULLABLE", iInt2Oid, 2);
1593     f[12] = new Field(connection, "REMARKS", iVarcharOid, 32);
1594     
1595     // add query loop here
1596     
1597     return new ResultSet(connection, f, v, "OK", 1);
1598   }
1599   
1600   /**
1601    * Get a description of tables available in a catalog.              
1602    *
1603    * <p>Only table descriptions matching the catalog, schema, table
1604    * name and type criteria are returned. They are ordered by
1605    * TABLE_TYPE, TABLE_SCHEM and TABLE_NAME.                      
1606    * 
1607    * <p>Each table description has the following columns:     
1608    *
1609    * <ol>
1610    * <li><b>TABLE_CAT</b> String => table catalog (may be null)      
1611    * <li><b>TABLE_SCHEM</b> String => table schema (may be null)         
1612    * <li><b>TABLE_NAME</b> String => table name
1613    * <li><b>TABLE_TYPE</b> String => table type. Typical types are "TABLE",
1614    * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL
1615    * TEMPORARY", "ALIAS", "SYNONYM".                             
1616    * <li><b>REMARKS</b> String => explanatory comment on the table
1617    * </ol>
1618    *
1619    * <p>The valid values for the types parameter are:
1620    * "TABLE", "INDEX", "LARGE OBJECT", "SEQUENCE", "SYSTEM TABLE" and
1621    * "SYSTEM INDEX"
1622    *
1623    * @param catalog a catalog name; For org.postgresql, this is ignored, and
1624    * should be set to null
1625    * @param schemaPattern a schema name pattern; For org.postgresql, this is ignored, and
1626    * should be set to null
1627    * @param tableNamePattern a table name pattern. For all tables this should be "%"
1628    * @param types a list of table types to include; null returns
1629    * all types
1630    * @return each row is a table description      
1631    * @exception SQLException if a database-access error occurs.
1632    */
1633   public java.sql.ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String types[]) throws SQLException
1634   {
1635     // Handle default value for types
1636     if(types==null)
1637       types = defaultTableTypes;
1638     
1639     if(tableNamePattern==null)
1640       tableNamePattern="%";
1641     
1642     // the field descriptors for the new ResultSet
1643     Field f[] = new Field[5];
1644     java.sql.ResultSet r;       // ResultSet for the SQL query that we need to do
1645     Vector v = new Vector();            // The new ResultSet tuple stuff
1646     
1647     f[0] = new Field(connection, "TABLE_CAT", iVarcharOid, 32);
1648     f[1] = new Field(connection, "TABLE_SCHEM", iVarcharOid, 32);
1649     f[2] = new Field(connection, "TABLE_NAME", iVarcharOid, 32);
1650     f[3] = new Field(connection, "TABLE_TYPE", iVarcharOid, 32);
1651     f[4] = new Field(connection, "REMARKS", iVarcharOid, 32);
1652     
1653     // Now form the query
1654     StringBuffer sql = new StringBuffer("select relname,oid,relkind from pg_class where (");
1655     boolean notFirst=false;
1656     for(int i=0;i<types.length;i++) {
1657       for(int j=0;j<getTableTypes.length;j++)
1658         if(getTableTypes[j][0].equals(types[i])) {
1659           if(notFirst)
1660             sql.append(" or ");
1661           sql.append(getTableTypes[j][1]);
1662           notFirst=true;
1663         }
1664     }
1665     
1666     // Added by Stefan Andreasen <stefan@linux.kapow.dk>
1667     // Now take the pattern into account
1668     sql.append(") and relname like '");
1669     sql.append(tableNamePattern.toLowerCase());
1670     sql.append("'");
1671     
1672     // Now run the query
1673     r = connection.ExecSQL(sql.toString());
1674     
1675     byte remarks[];
1676     
1677     while (r.next())
1678       {
1679         byte[][] tuple = new byte[5][0];
1680         
1681         // Fetch the description for the table (if any)
1682         java.sql.ResultSet dr = connection.ExecSQL("select description from pg_description where objoid="+r.getInt(2));
1683         if(((org.postgresql.ResultSet)dr).getTupleCount()==1) {
1684           dr.next();
1685           remarks = dr.getBytes(1);
1686         } else
1687           remarks = defaultRemarks;
1688         dr.close();
1689         
1690         String relKind;
1691         switch (r.getBytes(3)[0]) {
1692         case 'r':
1693             relKind = "TABLE";
1694             break;
1695         case 'i':
1696             relKind = "INDEX";
1697             break;
1698         case 'S':
1699             relKind = "SEQUENCE";
1700             break;
1701         default:
1702             relKind = null;
1703         }
1704
1705         tuple[0] = null;                // Catalog name
1706         tuple[1] = null;                // Schema name
1707         tuple[2] = r.getBytes(1);       // Table name   
1708         tuple[3] = relKind.getBytes();  // Table type
1709         tuple[4] = remarks;             // Remarks
1710         v.addElement(tuple);
1711       }
1712     r.close();
1713     return new ResultSet(connection, f, v, "OK", 1);
1714   }
1715   
1716   // This array contains the valid values for the types argument
1717   // in getTables().
1718   //
1719   // Each supported type consists of it's name, and the sql where
1720   // clause to retrieve that value.
1721   //
1722   // IMPORTANT: the query must be enclosed in ( )
1723   private static final String getTableTypes[][] = {
1724     {"TABLE",           "(relkind='r' and relhasrules='f' and relname !~ '^pg_' and relname !~ '^xinv')"},
1725     {"VIEW",        "(relkind='v' and relname !~ '^pg_' and relname !~ '^xinv')"},
1726     {"INDEX",           "(relkind='i' and relname !~ '^pg_' and relname !~ '^xinx')"},
1727     {"LARGE OBJECT",    "(relkind='r' and relname ~ '^xinv')"},
1728     {"SEQUENCE",        "(relkind='S' and relname !~ '^pg_')"},
1729     {"SYSTEM TABLE",    "(relkind='r' and relname ~ '^pg_')"},
1730     {"SYSTEM INDEX",    "(relkind='i' and relname ~ '^pg_')"}
1731   };
1732   
1733   // These are the default tables, used when NULL is passed to getTables
1734   // The choice of these provide the same behaviour as psql's \d
1735   private static final String defaultTableTypes[] = {
1736     "TABLE","VIEW","INDEX","SEQUENCE"
1737   };
1738   
1739   /**
1740    * Get the schema names available in this database.  The results
1741    * are ordered by schema name.
1742    *
1743    * <P>The schema column is:
1744    *  <OL>
1745    *    <LI><B>TABLE_SCHEM</B> String => schema name
1746    *  </OL>
1747    *
1748    * @return ResultSet each row has a single String column that is a
1749    * schema name
1750    */
1751   public java.sql.ResultSet getSchemas() throws SQLException
1752   {
1753     // We don't use schemas, so we simply return a single schema name "".
1754     //
1755     Field f[] = new Field[1];
1756     Vector v = new Vector();
1757     byte[][] tuple = new byte[1][0];
1758     f[0] = new Field(connection,"TABLE_SCHEM",iVarcharOid,32);
1759     tuple[0] = "".getBytes();
1760     v.addElement(tuple);
1761     return new ResultSet(connection,f,v,"OK",1);
1762   }
1763   
1764   /**
1765    * Get the catalog names available in this database.  The results
1766    * are ordered by catalog name.
1767    *
1768    * <P>The catalog column is:
1769    *  <OL>
1770    *    <LI><B>TABLE_CAT</B> String => catalog name
1771    *  </OL>
1772    *
1773    * @return ResultSet each row has a single String column that is a
1774    * catalog name
1775    */
1776   public java.sql.ResultSet getCatalogs() throws SQLException
1777   {
1778     // We don't use catalogs, so we simply return a single catalog name "".
1779     Field f[] = new Field[1];
1780     Vector v = new Vector();
1781     byte[][] tuple = new byte[1][0];
1782     f[0] = new Field(connection,"TABLE_CAT",iVarcharOid,32);
1783     tuple[0] = "".getBytes();
1784     v.addElement(tuple);
1785     return new ResultSet(connection,f,v,"OK",1);
1786   }
1787   
1788   /**
1789    * Get the table types available in this database.  The results
1790    * are ordered by table type.
1791    *
1792    * <P>The table type is:
1793    *  <OL>
1794    *    <LI><B>TABLE_TYPE</B> String => table type.  Typical types are "TABLE",
1795    *                    "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY",
1796    *                    "LOCAL TEMPORARY", "ALIAS", "SYNONYM".
1797    *  </OL>
1798    *
1799    * @return ResultSet each row has a single String column that is a
1800    * table type
1801    */
1802   public java.sql.ResultSet getTableTypes() throws SQLException
1803   {
1804     Field f[] = new Field[1];
1805     Vector v = new Vector();
1806     f[0] = new Field(connection,new String("TABLE_TYPE"),iVarcharOid,32);
1807     for(int i=0;i<getTableTypes.length;i++) {
1808       byte[][] tuple = new byte[1][0];
1809       tuple[0] = getTableTypes[i][0].getBytes();
1810       v.addElement(tuple);
1811     }
1812     return new ResultSet(connection,f,v,"OK",1);
1813   }
1814   
1815   /**
1816    * Get a description of table columns available in a catalog.
1817    *
1818    * <P>Only column descriptions matching the catalog, schema, table
1819    * and column name criteria are returned.  They are ordered by
1820    * TABLE_SCHEM, TABLE_NAME and ORDINAL_POSITION.
1821    *
1822    * <P>Each column description has the following columns:
1823    *  <OL>
1824    *    <LI><B>TABLE_CAT</B> String => table catalog (may be null)
1825    *    <LI><B>TABLE_SCHEM</B> String => table schema (may be null)
1826    *    <LI><B>TABLE_NAME</B> String => table name
1827    *    <LI><B>COLUMN_NAME</B> String => column name
1828    *    <LI><B>DATA_TYPE</B> short => SQL type from java.sql.Types
1829    *    <LI><B>TYPE_NAME</B> String => Data source dependent type name
1830    *    <LI><B>COLUMN_SIZE</B> int => column size.  For char or date
1831    *        types this is the maximum number of characters, for numeric or
1832    *        decimal types this is precision.
1833    *    <LI><B>BUFFER_LENGTH</B> is not used.
1834    *    <LI><B>DECIMAL_DIGITS</B> int => the number of fractional digits
1835    *    <LI><B>NUM_PREC_RADIX</B> int => Radix (typically either 10 or 2)
1836    *    <LI><B>NULLABLE</B> int => is NULL allowed?
1837    *      <UL>
1838    *      <LI> columnNoNulls - might not allow NULL values
1839    *      <LI> columnNullable - definitely allows NULL values
1840    *      <LI> columnNullableUnknown - nullability unknown
1841    *      </UL>
1842    *    <LI><B>REMARKS</B> String => comment describing column (may be null)
1843    *    <LI><B>COLUMN_DEF</B> String => default value (may be null)
1844    *    <LI><B>SQL_DATA_TYPE</B> int => unused
1845    *    <LI><B>SQL_DATETIME_SUB</B> int => unused
1846    *    <LI><B>CHAR_OCTET_LENGTH</B> int => for char types the
1847    *       maximum number of bytes in the column
1848    *    <LI><B>ORDINAL_POSITION</B> int => index of column in table
1849    *      (starting at 1)
1850    *    <LI><B>IS_NULLABLE</B> String => "NO" means column definitely
1851    *      does not allow NULL values; "YES" means the column might
1852    *      allow NULL values.  An empty string means nobody knows.
1853    *  </OL>
1854    *
1855    * @param catalog a catalog name; "" retrieves those without a catalog
1856    * @param schemaPattern a schema name pattern; "" retrieves those
1857    * without a schema
1858    * @param tableNamePattern a table name pattern
1859    * @param columnNamePattern a column name pattern
1860    * @return ResultSet each row is a column description
1861    * @see #getSearchStringEscape
1862    */
1863   public java.sql.ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException
1864   {
1865     // the field descriptors for the new ResultSet
1866     Field f[] = new Field[18];
1867     java.sql.ResultSet r;       // ResultSet for the SQL query that we need to do
1868     Vector v = new Vector();            // The new ResultSet tuple stuff
1869     
1870     f[0] = new Field(connection, "TABLE_CAT", iVarcharOid, 32);
1871     f[1] = new Field(connection, "TABLE_SCHEM", iVarcharOid, 32);
1872     f[2] = new Field(connection, "TABLE_NAME", iVarcharOid, 32);
1873     f[3] = new Field(connection, "COLUMN_NAME", iVarcharOid, 32);
1874     f[4] = new Field(connection, "DATA_TYPE", iInt2Oid, 2);
1875     f[5] = new Field(connection, "TYPE_NAME", iVarcharOid, 32);
1876     f[6] = new Field(connection, "COLUMN_SIZE", iInt4Oid, 4);
1877     f[7] = new Field(connection, "BUFFER_LENGTH", iVarcharOid, 32);
1878     f[8] = new Field(connection, "DECIMAL_DIGITS", iInt4Oid, 4);
1879     f[9] = new Field(connection, "NUM_PREC_RADIX", iInt4Oid, 4);
1880     f[10] = new Field(connection, "NULLABLE", iInt4Oid, 4);
1881     f[11] = new Field(connection, "REMARKS", iVarcharOid, 32);
1882     f[12] = new Field(connection, "COLUMN_DEF", iVarcharOid, 32);
1883     f[13] = new Field(connection, "SQL_DATA_TYPE", iInt4Oid, 4);
1884     f[14] = new Field(connection, "SQL_DATETIME_SUB", iInt4Oid, 4);
1885     f[15] = new Field(connection, "CHAR_OCTET_LENGTH", iVarcharOid, 32);
1886     f[16] = new Field(connection, "ORDINAL_POSITION", iInt4Oid,4);
1887     f[17] = new Field(connection, "IS_NULLABLE", iVarcharOid, 32);
1888     
1889     // Added by Stefan Andreasen <stefan@linux.kapow.dk>
1890     // If the pattern are  null then set them to %
1891     if (tableNamePattern == null) tableNamePattern="%";
1892     if (columnNamePattern == null) columnNamePattern="%";
1893     
1894     // Now form the query
1895     // Modified by Stefan Andreasen <stefan@linux.kapow.dk>
1896     r = connection.ExecSQL("select a.oid,c.relname,a.attname,a.atttypid,a.attnum,a.attnotnull,a.attlen,a.atttypmod from pg_class c, pg_attribute a where a.attrelid=c.oid and c.relname like '"+tableNamePattern.toLowerCase()+"' and a.attname like '"+columnNamePattern.toLowerCase()+"' and a.attnum>0 order by c.relname,a.attnum");
1897     
1898     byte remarks[];
1899     
1900     while(r.next()) {
1901         byte[][] tuple = new byte[18][0];
1902         
1903         // Fetch the description for the table (if any)
1904         java.sql.ResultSet dr = connection.ExecSQL("select description from pg_description where objoid="+r.getInt(1));
1905         if(((org.postgresql.ResultSet)dr).getTupleCount()==1) {
1906           dr.next();
1907           tuple[11] = dr.getBytes(1);
1908         } else
1909           tuple[11] = defaultRemarks;
1910         
1911         dr.close();
1912         
1913         tuple[0] = "".getBytes();       // Catalog name
1914         tuple[1] = "".getBytes();       // Schema name
1915         tuple[2] = r.getBytes(2);       // Table name
1916         tuple[3] = r.getBytes(3);       // Column name
1917         
1918         dr = connection.ExecSQL("select typname from pg_type where oid = "+r.getString(4));
1919         dr.next();
1920         String typname=dr.getString(1);
1921         dr.close();
1922         tuple[4] = Integer.toString(Field.getSQLType(typname)).getBytes();      // Data type
1923         tuple[5] = typname.getBytes();  // Type name
1924         
1925         // Column size
1926         // Looking at the psql source,
1927         // I think the length of a varchar as specified when the table was created
1928         // should be extracted from atttypmod which contains this length + sizeof(int32)
1929         if (typname.equals("bpchar") || typname.equals("varchar")) {
1930           int atttypmod = r.getInt(8);
1931           tuple[6] = Integer.toString(atttypmod != -1 ? atttypmod - VARHDRSZ : 0).getBytes();
1932         } else
1933           tuple[6] = r.getBytes(7);
1934         
1935         tuple[7] = null;        // Buffer length
1936         
1937         tuple[8] = "0".getBytes();      // Decimal Digits - how to get this?
1938         tuple[9] = "10".getBytes();     // Num Prec Radix - assume decimal
1939         
1940         // tuple[10] is below
1941         // tuple[11] is above
1942         
1943         tuple[12] = null;       // column default
1944         
1945         tuple[13] = null;       // sql data type (unused)
1946         tuple[14] = null;       // sql datetime sub (unused)
1947         
1948         tuple[15] = tuple[6];   // char octet length
1949         
1950         tuple[16] = r.getBytes(5);      // ordinal position
1951         
1952         String nullFlag = r.getString(6);
1953         tuple[10] = Integer.toString(nullFlag.equals("f")?java.sql.DatabaseMetaData.columnNullable:java.sql.DatabaseMetaData.columnNoNulls).getBytes(); // Nullable
1954         tuple[17] = (nullFlag.equals("f")?"YES":"NO").getBytes();       // is nullable
1955         
1956         v.addElement(tuple);
1957       }
1958     r.close();
1959     return new ResultSet(connection, f, v, "OK", 1);
1960   }
1961   
1962   /**
1963    * Get a description of the access rights for a table's columns.
1964    *
1965    * <P>Only privileges matching the column name criteria are
1966    * returned.  They are ordered by COLUMN_NAME and PRIVILEGE.
1967    *
1968    * <P>Each privilige description has the following columns:
1969    *  <OL>
1970    *    <LI><B>TABLE_CAT</B> String => table catalog (may be null)
1971    *    <LI><B>TABLE_SCHEM</B> String => table schema (may be null)
1972    *    <LI><B>TABLE_NAME</B> String => table name
1973    *    <LI><B>COLUMN_NAME</B> String => column name
1974    *    <LI><B>GRANTOR</B> => grantor of access (may be null)
1975    *    <LI><B>GRANTEE</B> String => grantee of access
1976    *    <LI><B>PRIVILEGE</B> String => name of access (SELECT,
1977    *      INSERT, UPDATE, REFRENCES, ...)
1978    *    <LI><B>IS_GRANTABLE</B> String => "YES" if grantee is permitted
1979    *      to grant to others; "NO" if not; null if unknown
1980    *  </OL>
1981    *
1982    * @param catalog a catalog name; "" retrieves those without a catalog
1983    * @param schema a schema name; "" retrieves those without a schema
1984    * @param table a table name
1985    * @param columnNamePattern a column name pattern
1986    * @return ResultSet each row is a column privilege description
1987    * @see #getSearchStringEscape
1988    */
1989   public java.sql.ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) throws SQLException
1990   {
1991     Field f[] = new Field[8];
1992     Vector v = new Vector();
1993     
1994     if(table==null)
1995       table="%";
1996     
1997     if(columnNamePattern==null)
1998       columnNamePattern="%";
1999     else
2000       columnNamePattern=columnNamePattern.toLowerCase();
2001     
2002     f[0] = new Field(connection,"TABLE_CAT",iVarcharOid,32);
2003     f[1] = new Field(connection,"TABLE_SCHEM",iVarcharOid,32);
2004     f[2] = new Field(connection,"TABLE_NAME",iVarcharOid,32);
2005     f[3] = new Field(connection,"COLUMN_NAME",iVarcharOid,32);
2006     f[4] = new Field(connection,"GRANTOR",iVarcharOid,32);
2007     f[5] = new Field(connection,"GRANTEE",iVarcharOid,32);
2008     f[6] = new Field(connection,"PRIVILEGE",iVarcharOid,32);
2009     f[7] = new Field(connection,"IS_GRANTABLE",iVarcharOid,32);
2010     
2011     // This is taken direct from the psql source
2012     java.sql.ResultSet r = connection.ExecSQL("SELECT relname, relacl FROM pg_class, pg_user WHERE ( relkind = 'r' OR relkind = 'i') and relname !~ '^pg_' and relname !~ '^xin[vx][0-9]+' and usesysid = relowner and relname like '"+table.toLowerCase()+"' ORDER BY relname");
2013     while(r.next()) {
2014       byte[][] tuple = new byte[8][0];
2015       tuple[0] = tuple[1]= "".getBytes();
2016       DriverManager.println("relname=\""+r.getString(1)+"\" relacl=\""+r.getString(2)+"\"");
2017       
2018       // For now, don't add to the result as relacl needs to be processed.
2019       //v.addElement(tuple);
2020     }
2021     
2022     return new ResultSet(connection,f,v,"OK",1);
2023   }
2024   
2025   /**
2026    * Get a description of the access rights for each table available
2027    * in a catalog.
2028    *
2029    * <P>Only privileges matching the schema and table name
2030    * criteria are returned.  They are ordered by TABLE_SCHEM,
2031    * TABLE_NAME, and PRIVILEGE.
2032    *
2033    * <P>Each privilige description has the following columns:
2034    *  <OL>
2035    *    <LI><B>TABLE_CAT</B> String => table catalog (may be null)
2036    *    <LI><B>TABLE_SCHEM</B> String => table schema (may be null)
2037    *    <LI><B>TABLE_NAME</B> String => table name
2038    *    <LI><B>COLUMN_NAME</B> String => column name
2039    *    <LI><B>GRANTOR</B> => grantor of access (may be null)
2040    *    <LI><B>GRANTEE</B> String => grantee of access
2041    *    <LI><B>PRIVILEGE</B> String => name of access (SELECT,
2042    *      INSERT, UPDATE, REFRENCES, ...)
2043    *    <LI><B>IS_GRANTABLE</B> String => "YES" if grantee is permitted
2044    *      to grant to others; "NO" if not; null if unknown
2045    *  </OL>
2046    *
2047    * @param catalog a catalog name; "" retrieves those without a catalog
2048    * @param schemaPattern a schema name pattern; "" retrieves those
2049    * without a schema
2050    * @param tableNamePattern a table name pattern
2051    * @return ResultSet each row is a table privilege description
2052    * @see #getSearchStringEscape
2053    */
2054   public java.sql.ResultSet getTablePrivileges(String catalog, String schemaPattern, String tableNamePattern) throws SQLException
2055   {
2056     // XXX-Not Implemented
2057     return null;
2058   }
2059   
2060   /**
2061    * Get a description of a table's optimal set of columns that
2062    * uniquely identifies a row. They are ordered by SCOPE.
2063    *
2064    * <P>Each column description has the following columns:
2065    *  <OL>
2066    *    <LI><B>SCOPE</B> short => actual scope of result
2067    *      <UL>
2068    *      <LI> bestRowTemporary - very temporary, while using row
2069    *      <LI> bestRowTransaction - valid for remainder of current transaction
2070    *      <LI> bestRowSession - valid for remainder of current session
2071    *      </UL>
2072    *    <LI><B>COLUMN_NAME</B> String => column name
2073    *    <LI><B>DATA_TYPE</B> short => SQL data type from java.sql.Types
2074    *    <LI><B>TYPE_NAME</B> String => Data source dependent type name
2075    *    <LI><B>COLUMN_SIZE</B> int => precision
2076    *    <LI><B>BUFFER_LENGTH</B> int => not used
2077    *    <LI><B>DECIMAL_DIGITS</B> short  => scale
2078    *    <LI><B>PSEUDO_COLUMN</B> short => is this a pseudo column
2079    *      like an Oracle ROWID
2080    *      <UL>
2081    *      <LI> bestRowUnknown - may or may not be pseudo column
2082    *      <LI> bestRowNotPseudo - is NOT a pseudo column
2083    *      <LI> bestRowPseudo - is a pseudo column
2084    *      </UL>
2085    *  </OL>
2086    *
2087    * @param catalog a catalog name; "" retrieves those without a catalog
2088    * @param schema a schema name; "" retrieves those without a schema
2089    * @param table a table name
2090    * @param scope the scope of interest; use same values as SCOPE
2091    * @param nullable include columns that are nullable?
2092    * @return ResultSet each row is a column description
2093    */
2094   // Implementation note: This is required for Borland's JBuilder to work
2095   public java.sql.ResultSet getBestRowIdentifier(String catalog, String schema, String table, int scope, boolean nullable) throws SQLException
2096   {
2097     // for now, this returns an empty result set.
2098     Field f[] = new Field[8];
2099     ResultSet r;        // ResultSet for the SQL query that we need to do
2100     Vector v = new Vector();            // The new ResultSet tuple stuff
2101     
2102     f[0] = new Field(connection, "SCOPE", iInt2Oid, 2);
2103     f[1] = new Field(connection, "COLUMN_NAME", iVarcharOid, 32);
2104     f[2] = new Field(connection, "DATA_TYPE", iInt2Oid, 2);
2105     f[3] = new Field(connection, "TYPE_NAME", iVarcharOid, 32);
2106     f[4] = new Field(connection, "COLUMN_SIZE", iInt4Oid, 4);
2107     f[5] = new Field(connection, "BUFFER_LENGTH", iInt4Oid, 4);
2108     f[6] = new Field(connection, "DECIMAL_DIGITS", iInt2Oid, 2);
2109     f[7] = new Field(connection, "PSEUDO_COLUMN", iInt2Oid, 2);
2110     
2111     return new ResultSet(connection, f, v, "OK", 1);
2112   }
2113   
2114   /**
2115    * Get a description of a table's columns that are automatically
2116    * updated when any value in a row is updated.  They are
2117    * unordered.
2118    *
2119    * <P>Each column description has the following columns:
2120    *  <OL>
2121    *    <LI><B>SCOPE</B> short => is not used
2122    *    <LI><B>COLUMN_NAME</B> String => column name
2123    *    <LI><B>DATA_TYPE</B> short => SQL data type from java.sql.Types
2124    *    <LI><B>TYPE_NAME</B> String => Data source dependent type name
2125    *    <LI><B>COLUMN_SIZE</B> int => precision
2126    *    <LI><B>BUFFER_LENGTH</B> int => length of column value in bytes
2127    *    <LI><B>DECIMAL_DIGITS</B> short  => scale
2128    *    <LI><B>PSEUDO_COLUMN</B> short => is this a pseudo column
2129    *      like an Oracle ROWID
2130    *      <UL>
2131    *      <LI> versionColumnUnknown - may or may not be pseudo column
2132    *      <LI> versionColumnNotPseudo - is NOT a pseudo column
2133    *      <LI> versionColumnPseudo - is a pseudo column
2134    *      </UL>
2135    *  </OL>
2136    *
2137    * @param catalog a catalog name; "" retrieves those without a catalog
2138    * @param schema a schema name; "" retrieves those without a schema
2139    * @param table a table name
2140    * @return ResultSet each row is a column description
2141    */
2142  public java.sql.ResultSet getVersionColumns(String catalog, String schema, String table) throws SQLException
2143   {
2144     // XXX-Not Implemented
2145     return null;
2146   }
2147   
2148   /**
2149    * Get a description of a table's primary key columns.  They
2150    * are ordered by COLUMN_NAME.
2151    *
2152    * <P>Each column description has the following columns:
2153    *  <OL>
2154    *    <LI><B>TABLE_CAT</B> String => table catalog (may be null)
2155    *    <LI><B>TABLE_SCHEM</B> String => table schema (may be null)
2156    *    <LI><B>TABLE_NAME</B> String => table name
2157    *    <LI><B>COLUMN_NAME</B> String => column name
2158    *    <LI><B>KEY_SEQ</B> short => sequence number within primary key
2159    *    <LI><B>PK_NAME</B> String => primary key name (may be null)
2160    *  </OL>
2161    *
2162    * @param catalog a catalog name; "" retrieves those without a catalog
2163    * @param schema a schema name pattern; "" retrieves those
2164    * without a schema
2165    * @param table a table name
2166    * @return ResultSet each row is a primary key column description
2167    */
2168   public java.sql.ResultSet getPrimaryKeys(String catalog, String schema, String table) throws SQLException
2169   {
2170     return connection.createStatement().executeQuery("SELECT " +
2171                                                      "'' as TABLE_CAT," +
2172                                                      "'' AS TABLE_SCHEM," +
2173                                                      "bc.relname AS TABLE_NAME," +
2174                                                      "a.attname AS COLUMN_NAME," +
2175                                                      "a.attnum as KEY_SEQ,"+
2176                                                      "ic.relname as PK_NAME " +
2177                                                      " FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a" +
2178                                                      " WHERE bc.relkind = 'r' " + //    -- not indices
2179                                                      "  and upper(bc.relname) = upper('"+table+"')" +
2180                                                      "  and i.indrelid = bc.oid" +
2181                                                      "  and i.indexrelid = ic.oid" +
2182                                                      "  and ic.oid = a.attrelid" +
2183                                                      "  and i.indisprimary='t' " +
2184                                                      " ORDER BY table_name, pk_name, key_seq"
2185                                                      );
2186   }
2187   
2188   /**
2189    * Get a description of the primary key columns that are
2190    * referenced by a table's foreign key columns (the primary keys
2191    * imported by a table).  They are ordered by PKTABLE_CAT,
2192    * PKTABLE_SCHEM, PKTABLE_NAME, and KEY_SEQ.
2193    *
2194    * <P>Each primary key column description has the following columns:
2195    *  <OL>
2196    *    <LI><B>PKTABLE_CAT</B> String => primary key table catalog
2197    *      being imported (may be null)
2198    *    <LI><B>PKTABLE_SCHEM</B> String => primary key table schema
2199    *      being imported (may be null)
2200    *    <LI><B>PKTABLE_NAME</B> String => primary key table name
2201    *      being imported
2202    *    <LI><B>PKCOLUMN_NAME</B> String => primary key column name
2203    *      being imported
2204    *    <LI><B>FKTABLE_CAT</B> String => foreign key table catalog (may be null)
2205    *    <LI><B>FKTABLE_SCHEM</B> String => foreign key table schema (may be null)
2206    *    <LI><B>FKTABLE_NAME</B> String => foreign key table name
2207    *    <LI><B>FKCOLUMN_NAME</B> String => foreign key column name
2208    *    <LI><B>KEY_SEQ</B> short => sequence number within foreign key
2209    *    <LI><B>UPDATE_RULE</B> short => What happens to
2210    *       foreign key when primary is updated:
2211    *      <UL>
2212    *      <LI> importedKeyCascade - change imported key to agree
2213    *               with primary key update
2214    *      <LI> importedKeyRestrict - do not allow update of primary
2215    *               key if it has been imported
2216    *      <LI> importedKeySetNull - change imported key to NULL if
2217    *               its primary key has been updated
2218    *      </UL>
2219    *    <LI><B>DELETE_RULE</B> short => What happens to
2220    *      the foreign key when primary is deleted.
2221    *      <UL>
2222    *      <LI> importedKeyCascade - delete rows that import a deleted key
2223    *      <LI> importedKeyRestrict - do not allow delete of primary
2224    *               key if it has been imported
2225    *      <LI> importedKeySetNull - change imported key to NULL if
2226    *               its primary key has been deleted
2227    *      </UL>
2228    *    <LI><B>FK_NAME</B> String => foreign key name (may be null)
2229    *    <LI><B>PK_NAME</B> String => primary key name (may be null)
2230    *  </OL>
2231    *
2232    * @param catalog a catalog name; "" retrieves those without a catalog
2233    * @param schema a schema name pattern; "" retrieves those
2234    * without a schema
2235    * @param table a table name
2236    * @return ResultSet each row is a primary key column description
2237    * @see #getExportedKeys
2238    */
2239   public java.sql.ResultSet getImportedKeys(String catalog, String schema, String table) throws SQLException
2240   {
2241     // XXX-Not Implemented
2242     return null;
2243   }
2244   
2245   /**
2246    * Get a description of a foreign key columns that reference a
2247    * table's primary key columns (the foreign keys exported by a
2248    * table).  They are ordered by FKTABLE_CAT, FKTABLE_SCHEM,
2249    * FKTABLE_NAME, and KEY_SEQ.
2250    *
2251    * <P>Each foreign key column description has the following columns:
2252    *  <OL>
2253    *    <LI><B>PKTABLE_CAT</B> String => primary key table catalog (may be null)
2254    *    <LI><B>PKTABLE_SCHEM</B> String => primary key table schema (may be null)
2255    *    <LI><B>PKTABLE_NAME</B> String => primary key table name
2256    *    <LI><B>PKCOLUMN_NAME</B> String => primary key column name
2257    *    <LI><B>FKTABLE_CAT</B> String => foreign key table catalog (may be null)
2258    *      being exported (may be null)
2259    *    <LI><B>FKTABLE_SCHEM</B> String => foreign key table schema (may be null)
2260    *      being exported (may be null)
2261    *    <LI><B>FKTABLE_NAME</B> String => foreign key table name
2262    *      being exported
2263    *    <LI><B>FKCOLUMN_NAME</B> String => foreign key column name
2264    *      being exported
2265    *    <LI><B>KEY_SEQ</B> short => sequence number within foreign key
2266    *    <LI><B>UPDATE_RULE</B> short => What happens to
2267    *       foreign key when primary is updated:
2268    *      <UL>
2269    *      <LI> importedKeyCascade - change imported key to agree
2270    *               with primary key update
2271    *      <LI> importedKeyRestrict - do not allow update of primary
2272    *               key if it has been imported
2273    *      <LI> importedKeySetNull - change imported key to NULL if
2274    *               its primary key has been updated
2275    *      </UL>
2276    *    <LI><B>DELETE_RULE</B> short => What happens to
2277    *      the foreign key when primary is deleted.
2278    *      <UL>
2279    *      <LI> importedKeyCascade - delete rows that import a deleted key
2280    *      <LI> importedKeyRestrict - do not allow delete of primary
2281    *               key if it has been imported
2282    *      <LI> importedKeySetNull - change imported key to NULL if
2283    *               its primary key has been deleted
2284    *      </UL>
2285    *    <LI><B>FK_NAME</B> String => foreign key identifier (may be null)
2286    *    <LI><B>PK_NAME</B> String => primary key identifier (may be null)
2287    *  </OL>
2288    *
2289    * @param catalog a catalog name; "" retrieves those without a catalog
2290    * @param schema a schema name pattern; "" retrieves those
2291    * without a schema
2292    * @param table a table name
2293    * @return ResultSet each row is a foreign key column description
2294    * @see #getImportedKeys
2295    */
2296   public java.sql.ResultSet getExportedKeys(String catalog, String schema, String table) throws SQLException
2297   {
2298     // XXX-Not Implemented
2299     return null;
2300   }
2301   
2302   /**
2303    * Get a description of the foreign key columns in the foreign key
2304    * table that reference the primary key columns of the primary key
2305    * table (describe how one table imports another's key.) This
2306    * should normally return a single foreign key/primary key pair
2307    * (most tables only import a foreign key from a table once.)  They
2308    * are ordered by FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, and
2309    * KEY_SEQ.
2310    *
2311    * <P>Each foreign key column description has the following columns:
2312    *  <OL>
2313    *    <LI><B>PKTABLE_CAT</B> String => primary key table catalog (may be null)
2314    *    <LI><B>PKTABLE_SCHEM</B> String => primary key table schema (may be null)
2315    *    <LI><B>PKTABLE_NAME</B> String => primary key table name
2316    *    <LI><B>PKCOLUMN_NAME</B> String => primary key column name
2317    *    <LI><B>FKTABLE_CAT</B> String => foreign key table catalog (may be null)
2318    *      being exported (may be null)
2319    *    <LI><B>FKTABLE_SCHEM</B> String => foreign key table schema (may be null)
2320    *      being exported (may be null)
2321    *    <LI><B>FKTABLE_NAME</B> String => foreign key table name
2322    *      being exported
2323    *    <LI><B>FKCOLUMN_NAME</B> String => foreign key column name
2324    *      being exported
2325    *    <LI><B>KEY_SEQ</B> short => sequence number within foreign key
2326    *    <LI><B>UPDATE_RULE</B> short => What happens to
2327    *       foreign key when primary is updated:
2328    *      <UL>
2329    *      <LI> importedKeyCascade - change imported key to agree
2330    *               with primary key update
2331    *      <LI> importedKeyRestrict - do not allow update of primary
2332    *               key if it has been imported
2333    *      <LI> importedKeySetNull - change imported key to NULL if
2334    *               its primary key has been updated
2335    *      </UL>
2336    *    <LI><B>DELETE_RULE</B> short => What happens to
2337    *      the foreign key when primary is deleted.
2338    *      <UL>
2339    *      <LI> importedKeyCascade - delete rows that import a deleted key
2340    *      <LI> importedKeyRestrict - do not allow delete of primary
2341    *               key if it has been imported
2342    *      <LI> importedKeySetNull - change imported key to NULL if
2343    *               its primary key has been deleted
2344    *      </UL>
2345    *    <LI><B>FK_NAME</B> String => foreign key identifier (may be null)
2346    *    <LI><B>PK_NAME</B> String => primary key identifier (may be null)
2347    *  </OL>
2348    *
2349    * @param catalog a catalog name; "" retrieves those without a catalog
2350    * @param schema a schema name pattern; "" retrieves those
2351    * without a schema
2352    * @param table a table name
2353    * @return ResultSet each row is a foreign key column description
2354    * @see #getImportedKeys
2355    */
2356   public java.sql.ResultSet getCrossReference(String primaryCatalog, String primarySchema, String primaryTable, String foreignCatalog, String foreignSchema, String foreignTable) throws SQLException
2357   {
2358     // XXX-Not Implemented
2359     return null;
2360   }
2361   
2362   /**
2363    * Get a description of all the standard SQL types supported by
2364    * this database. They are ordered by DATA_TYPE and then by how
2365    * closely the data type maps to the corresponding JDBC SQL type.
2366    *
2367    * <P>Each type description has the following columns:
2368    *  <OL>
2369    *    <LI><B>TYPE_NAME</B> String => Type name
2370    *    <LI><B>DATA_TYPE</B> short => SQL data type from java.sql.Types
2371    *    <LI><B>PRECISION</B> int => maximum precision
2372    *    <LI><B>LITERAL_PREFIX</B> String => prefix used to quote a literal
2373    *      (may be null)
2374    *    <LI><B>LITERAL_SUFFIX</B> String => suffix used to quote a literal
2375    (may be null)
2376    *    <LI><B>CREATE_PARAMS</B> String => parameters used in creating
2377    *      the type (may be null)
2378    *    <LI><B>NULLABLE</B> short => can you use NULL for this type?
2379    *      <UL>
2380    *      <LI> typeNoNulls - does not allow NULL values
2381    *      <LI> typeNullable - allows NULL values
2382    *      <LI> typeNullableUnknown - nullability unknown
2383    *      </UL>
2384    *    <LI><B>CASE_SENSITIVE</B> boolean=> is it case sensitive?
2385    *    <LI><B>SEARCHABLE</B> short => can you use "WHERE" based on this type:
2386    *      <UL>
2387    *      <LI> typePredNone - No support
2388    *      <LI> typePredChar - Only supported with WHERE .. LIKE
2389    *      <LI> typePredBasic - Supported except for WHERE .. LIKE
2390    *      <LI> typeSearchable - Supported for all WHERE ..
2391    *      </UL>
2392    *    <LI><B>UNSIGNED_ATTRIBUTE</B> boolean => is it unsigned?
2393    *    <LI><B>FIXED_PREC_SCALE</B> boolean => can it be a money value?
2394    *    <LI><B>AUTO_INCREMENT</B> boolean => can it be used for an
2395    *      auto-increment value?
2396    *    <LI><B>LOCAL_TYPE_NAME</B> String => localized version of type name
2397    *      (may be null)
2398    *    <LI><B>MINIMUM_SCALE</B> short => minimum scale supported
2399    *    <LI><B>MAXIMUM_SCALE</B> short => maximum scale supported
2400    *    <LI><B>SQL_DATA_TYPE</B> int => unused
2401    *    <LI><B>SQL_DATETIME_SUB</B> int => unused
2402    *    <LI><B>NUM_PREC_RADIX</B> int => usually 2 or 10
2403    *  </OL>
2404    *
2405    * @return ResultSet each row is a SQL type description
2406    */
2407   public java.sql.ResultSet getTypeInfo() throws SQLException
2408   {
2409     java.sql.ResultSet rs = connection.ExecSQL("select typname from pg_type");
2410     if(rs!=null) {
2411       Field f[] = new Field[18];
2412       ResultSet r;      // ResultSet for the SQL query that we need to do
2413       Vector v = new Vector();          // The new ResultSet tuple stuff
2414       
2415       f[0] = new Field(connection, "TYPE_NAME", iVarcharOid, 32);
2416       f[1] = new Field(connection, "DATA_TYPE", iInt2Oid, 2);
2417       f[2] = new Field(connection, "PRECISION", iInt4Oid, 4);
2418       f[3] = new Field(connection, "LITERAL_PREFIX", iVarcharOid, 32);
2419       f[4] = new Field(connection, "LITERAL_SUFFIX", iVarcharOid, 32);
2420       f[5] = new Field(connection, "CREATE_PARAMS", iVarcharOid, 32);
2421       f[6] = new Field(connection, "NULLABLE", iInt2Oid, 2);
2422       f[7] = new Field(connection, "CASE_SENSITIVE", iBoolOid, 1);
2423       f[8] = new Field(connection, "SEARCHABLE", iInt2Oid, 2);
2424       f[9] = new Field(connection, "UNSIGNED_ATTRIBUTE", iBoolOid, 1);
2425       f[10] = new Field(connection, "FIXED_PREC_SCALE", iBoolOid, 1);
2426       f[11] = new Field(connection, "AUTO_INCREMENT", iBoolOid, 1);
2427       f[12] = new Field(connection, "LOCAL_TYPE_NAME", iVarcharOid, 32);
2428       f[13] = new Field(connection, "MINIMUM_SCALE", iInt2Oid, 2);
2429       f[14] = new Field(connection, "MAXIMUM_SCALE", iInt2Oid, 2);
2430       f[15] = new Field(connection, "SQL_DATA_TYPE", iInt4Oid, 4);
2431       f[16] = new Field(connection, "SQL_DATETIME_SUB", iInt4Oid, 4);
2432       f[17] = new Field(connection, "NUM_PREC_RADIX", iInt4Oid, 4);
2433       
2434       // cache some results, this will keep memory useage down, and speed
2435       // things up a little.
2436       byte b9[]  = "9".getBytes();
2437       byte b10[] = "10".getBytes();
2438       byte bf[]  = "f".getBytes();
2439       byte bnn[] = Integer.toString(typeNoNulls).getBytes();
2440       byte bts[] = Integer.toString(typeSearchable).getBytes();
2441       
2442       while(rs.next()) {
2443         byte[][] tuple = new byte[18][];
2444         String typname=rs.getString(1);
2445         tuple[0] = typname.getBytes();
2446         tuple[1] = Integer.toString(Field.getSQLType(typname)).getBytes();
2447         tuple[2] = b9;  // for now
2448         tuple[6] = bnn; // for now
2449         tuple[7] = bf; // false for now - not case sensitive
2450         tuple[8] = bts;
2451         tuple[9] = bf; // false for now - it's signed
2452         tuple[10] = bf; // false for now - must handle money
2453         tuple[11] = bf; // false for now - handle autoincrement
2454         // 12 - LOCAL_TYPE_NAME is null
2455         // 13 & 14 ?
2456         // 15 & 16 are unused so we return null
2457         tuple[17] = b10; // everything is base 10
2458         v.addElement(tuple);
2459       }
2460       rs.close();
2461       return new ResultSet(connection, f, v, "OK", 1);
2462     }
2463     
2464     return null;
2465   }
2466   
2467   /**
2468    * Get a description of a table's indices and statistics. They are
2469    * ordered by NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION.
2470    *
2471    * <P>Each index column description has the following columns:
2472    *  <OL>
2473    *    <LI><B>TABLE_CAT</B> String => table catalog (may be null)
2474    *    <LI><B>TABLE_SCHEM</B> String => table schema (may be null)
2475    *    <LI><B>TABLE_NAME</B> String => table name
2476    *    <LI><B>NON_UNIQUE</B> boolean => Can index values be non-unique?
2477    *      false when TYPE is tableIndexStatistic
2478    *    <LI><B>INDEX_QUALIFIER</B> String => index catalog (may be null);
2479    *      null when TYPE is tableIndexStatistic
2480    *    <LI><B>INDEX_NAME</B> String => index name; null when TYPE is
2481    *      tableIndexStatistic
2482    *    <LI><B>TYPE</B> short => index type:
2483    *      <UL>
2484    *      <LI> tableIndexStatistic - this identifies table statistics that are
2485    *           returned in conjuction with a table's index descriptions
2486    *      <LI> tableIndexClustered - this is a clustered index
2487    *      <LI> tableIndexHashed - this is a hashed index
2488    *      <LI> tableIndexOther - this is some other style of index
2489    *      </UL>
2490    *    <LI><B>ORDINAL_POSITION</B> short => column sequence number
2491    *      within index; zero when TYPE is tableIndexStatistic
2492    *    <LI><B>COLUMN_NAME</B> String => column name; null when TYPE is
2493    *      tableIndexStatistic
2494    *    <LI><B>ASC_OR_DESC</B> String => column sort sequence, "A" => ascending
2495    *      "D" => descending, may be null if sort sequence is not supported;
2496    *      null when TYPE is tableIndexStatistic
2497    *    <LI><B>CARDINALITY</B> int => When TYPE is tableIndexStatisic then
2498    *      this is the number of rows in the table; otherwise it is the
2499    *      number of unique values in the index.
2500    *    <LI><B>PAGES</B> int => When TYPE is  tableIndexStatisic then
2501    *      this is the number of pages used for the table, otherwise it
2502    *      is the number of pages used for the current index.
2503    *    <LI><B>FILTER_CONDITION</B> String => Filter condition, if any.
2504    *      (may be null)
2505    *  </OL>
2506    *
2507    * @param catalog a catalog name; "" retrieves those without a catalog
2508    * @param schema a schema name pattern; "" retrieves those without a schema
2509    * @param table a table name
2510    * @param unique when true, return only indices for unique values;
2511    *     when false, return indices regardless of whether unique or not
2512    * @param approximate when true, result is allowed to reflect approximate
2513    *     or out of data values; when false, results are requested to be
2514    *     accurate
2515    * @return ResultSet each row is an index column description
2516    */
2517   // Implementation note: This is required for Borland's JBuilder to work
2518   public java.sql.ResultSet getIndexInfo(String catalog, String schema, String table, boolean unique, boolean approximate) throws SQLException
2519   {
2520     // for now, this returns an empty result set.
2521     Field f[] = new Field[13];
2522     ResultSet r;        // ResultSet for the SQL query that we need to do
2523     Vector v = new Vector();            // The new ResultSet tuple stuff
2524     
2525     f[0] = new Field(connection, "TABLE_CAT", iVarcharOid, 32);
2526     f[1] = new Field(connection, "TABLE_SCHEM", iVarcharOid, 32);
2527     f[2] = new Field(connection, "TABLE_NAME", iVarcharOid, 32);
2528     f[3] = new Field(connection, "NON_UNIQUE", iBoolOid, 1);
2529     f[4] = new Field(connection, "INDEX_QUALIFIER", iVarcharOid, 32);
2530     f[5] = new Field(connection, "INDEX_NAME", iVarcharOid, 32);
2531     f[6] = new Field(connection, "TYPE", iInt2Oid, 2);
2532     f[7] = new Field(connection, "ORDINAL_POSITION", iInt2Oid, 2);
2533     f[8] = new Field(connection, "COLUMN_NAME", iVarcharOid, 32);
2534     f[9] = new Field(connection, "ASC_OR_DESC", iVarcharOid, 32);
2535     f[10] = new Field(connection, "CARDINALITY", iInt4Oid, 4);
2536     f[11] = new Field(connection, "PAGES", iInt4Oid, 4);
2537     f[12] = new Field(connection, "FILTER_CONDITION", iVarcharOid, 32);
2538     
2539     return new ResultSet(connection, f, v, "OK", 1);
2540   }
2541 }
2542