1 /*------------------------------------------------------------------------------
2 Name: DbMetaHelper.java
3 Project: xmlBlaster.org
4 Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file
5 ------------------------------------------------------------------------------*/
6
7 package org.xmlBlaster.contrib.db;
8
9 import java.sql.Connection;
10 import java.sql.DatabaseMetaData;
11 import java.sql.ResultSet;
12 import java.sql.SQLException;
13 import java.util.TreeMap;
14
15 /**
16 *
17 * DbMetaHelper offers methods which take care of stuff specific to particular databases. For
18 * example if a database stores table names in uppercase you don't need to bother about this
19 * knowledge. You just pass the name of the table to the method getUnquotedIdentifier and you
20 * get back the correct name (if uppercase it will be uppercase).
21 *
22 * @author <a href="mailto:michele@laghi.eu">Michele Laghi</a>
23 */
24 public class DbMetaHelper {
25
26 private final static int CASE_UNKNOWN = 0;
27 private final static int CASE_MIXED = 1;
28 private final static int CASE_UPPER = 2;
29 private final static int CASE_LOWER = 3;
30
31 private int caseSense = CASE_UNKNOWN;
32
33 private int maxProcLength;
34 private String productName;
35
36 /**
37 * Initializes the object by reading the metadata of this database.
38 * @param pool the pool must be non null and initialized.
39 * @throws Exception If a backend exception occurs.
40 *
41 */
42 public DbMetaHelper(I_DbPool pool) throws Exception {
43 if (pool == null)
44 throw new Exception("DbMetaHelper constructor: the pool is null");
45 Connection conn = null;
46 try {
47 conn = pool.reserve();
48 DatabaseMetaData meta = conn.getMetaData();
49
50 this.maxProcLength = meta.getMaxProcedureNameLength();
51
52 if (meta.storesLowerCaseIdentifiers())
53 this.caseSense = CASE_LOWER;
54 else if (meta.storesUpperCaseIdentifiers())
55 this.caseSense = CASE_UPPER;
56 else if (meta.storesMixedCaseIdentifiers())
57 this.caseSense = CASE_MIXED;
58 else
59 throw new Exception("DbMetaHelper constructor: can not determine which case the identifiers are stored");
60 String tmp = meta.getDatabaseProductName();
61 if (tmp != null)
62 this.productName = tmp.trim().toUpperCase();
63 else
64 this.productName = "";
65 }
66 finally {
67 if (conn != null)
68 pool.release(conn);
69 }
70 }
71
72
73 /**
74 * Returns an array of column names correctly sorted of the specified table.
75 * Never returns null, if no columns found it returns an empty array.
76 * @throws Exception If a backend exception occurs.
77 *
78 */
79 public String[] getColumnNames(I_DbPool pool, String catalog, String schema, String table) throws Exception {
80 if (pool == null)
81 throw new Exception("DbMetaHelper.getColumnNames: the pool is null");
82 Connection conn = null;
83 ResultSet rs = null;
84 int count = 0;
85 try {
86 conn = pool.reserve();
87 DatabaseMetaData meta = conn.getMetaData();
88 if (catalog != null)
89 catalog = getIdentifier(catalog.trim());
90 if (schema != null)
91 schema = getIdentifier(schema.trim());
92 if (table != null)
93 table = getIdentifier(table.trim());
94 TreeMap map = new TreeMap();
95 rs = meta.getColumns(catalog, schema, table, null);
96 while (rs.next()) {
97 int pos = rs.getInt("ORDINAL_POSITION");
98 String name = rs.getString("COLUMN_NAME");
99 // should already be in the correct order according to
100 // javadoc but to be really sure we order it too
101 map.put(new Integer(pos), name);
102 count++;
103 }
104 if (count != map.size())
105 throw new Exception("Probably multiple tables '" + table + "' found since more columns with same ordinal position found");
106 return (String[])map.values().toArray(new String[map.size()]);
107 }
108 finally {
109 try {
110 if (rs != null)
111 rs.close();
112 }
113 catch (SQLException ex) {
114 ex.printStackTrace();
115 }
116 if (conn != null)
117 pool.release(conn);
118 }
119 }
120
121
122 /**
123 * Returns the correct identifier depending on the properties of the database. If it can not
124 * determine the case of the identifier it returns null.
125 * @param proposedName the name which is proposed.
126 * @return
127 */
128 public String getIdentifier(String proposedName) {
129 if (proposedName == null)
130 return null;
131 switch (this.caseSense) {
132 case CASE_MIXED : return proposedName;
133 case CASE_UPPER : return proposedName.toUpperCase();
134 case CASE_LOWER : return proposedName.toLowerCase();
135 default:return null;
136 }
137 }
138
139
140 /**
141 * If the name is too long it cuts first from the end of the schema, and then from the end of the
142 * table name. Otherwise it is ${PREFIX}${SEP}${SCHEMA}${SEP}${TABLENAME}
143 * @param schema
144 * @param tableName
145 * @return
146 */
147 public String createFunctionName(String prefix, String separator, String schema, String tableName) {
148 if (schema == null)
149 schema = "";
150 if (prefix == null)
151 prefix = "";
152 if (separator == null)
153 separator = "";
154 StringBuffer buf = new StringBuffer(this.maxProcLength);
155 buf.append(getIdentifier(prefix)).append(separator).append(getIdentifier(schema)).append(separator).append(getIdentifier(tableName));
156 if (buf.length() < this.maxProcLength)
157 return buf.toString();
158 int toCut = buf.length() - this.maxProcLength;
159 if (toCut >= schema.length()) {
160 toCut -= schema.length();
161 schema = "";
162 }
163 else {
164 schema = schema.substring(0, schema.length() - toCut);
165 }
166 if (toCut > 0)
167 tableName = tableName.substring(0, tableName.length() - toCut);
168 return createFunctionName(prefix, separator, schema, tableName);
169 }
170
171 public boolean isOracle() {
172 return this.productName.indexOf("ORACLE") != -1;
173 }
174 }
syntax highlighted by Code2HTML, v. 0.9.1