1 /***
2 * Redistribution and use of this software and associated documentation
3 * ("Software"), with or without modification, are permitted provided
4 * that the following conditions are met:
5 *
6 * 1. Redistributions of source code must retain copyright
7 * statements and notices. Redistributions must also contain a
8 * copy of this document.
9 *
10 * 2. Redistributions in binary form must reproduce the
11 * above copyright notice, this list of conditions and the
12 * following disclaimer in the documentation and/or other
13 * materials provided with the distribution.
14 *
15 * 3. The name "Exolab" must not be used to endorse or promote
16 * products derived from this Software without prior written
17 * permission of Exoffice Technologies. For written permission,
18 * please contact info@exolab.org.
19 *
20 * 4. Products derived from this Software may not be called "Exolab"
21 * nor may "Exolab" appear in their names without prior written
22 * permission of Exoffice Technologies. Exolab is a registered
23 * trademark of Exoffice Technologies.
24 *
25 * 5. Due credit should be given to the Exolab Project
26 * (http://www.exolab.org/).
27 *
28 * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS
29 * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
32 * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39 * OF THE POSSIBILITY OF SUCH DAMAGE.
40 *
41 * Copyright 2002-2005 (C) Exoffice Technologies Inc. All Rights Reserved.
42 *
43 * $Id: SchemaBrowser.java,v 1.2 2005/09/04 07:16:22 tanderson Exp $
44 */
45 package org.exolab.jms.tools.db;
46
47 import java.sql.Connection;
48 import java.sql.DatabaseMetaData;
49 import java.sql.PreparedStatement;
50 import java.sql.ResultSet;
51 import java.sql.ResultSetMetaData;
52 import java.sql.SQLException;
53
54 import org.exolab.jms.persistence.PersistenceException;
55 import org.exolab.jms.persistence.SQLHelper;
56
57
58 /***
59 * This class provides methods for examining a database schema.
60 *
61 * @author <a href="mailto:tma@netspace.net.au">Tim Anderson</a>
62 * @version $Revision: 1.2 $ $Date: 2005/09/04 07:16:22 $
63 */
64 public class SchemaBrowser {
65
66 /***
67 * The connection to the database.
68 */
69 private final Connection _connection;
70
71 /***
72 * The set of data types supported by the RDBMS.
73 */
74 private final TypeSet _types;
75
76 /***
77 * The type mapper, used to convert between the type requested by the
78 * schema, and those supported by the RDBMS.
79 */
80 private final TypeMapper _mapper;
81
82
83 /***
84 * Construct a new <code>SchemaBrowser</code>.
85 *
86 * @param connection the JDBC connection
87 * @throws PersistenceException if database meta-data can't be obtained
88 */
89 public SchemaBrowser(Connection connection) throws PersistenceException {
90 _connection = connection;
91
92
93 _types = new TypeSet(_connection);
94 _mapper = new TypeMapper(_types);
95 }
96
97 /***
98 * Returns the schema for the specified table.
99 *
100 * @param name the table name
101 * @return the schema for the table identified by <code>name</code>
102 * @throws PersistenceException if the named table doesn't exist, or the
103 * schema cannot be obtained
104 */
105 public Table getTable(String name) throws PersistenceException {
106 Table result = new Table();
107
108 result.setName(name);
109
110
111
112
113 PreparedStatement select = null;
114 try {
115 select = _connection.prepareStatement(
116 "select * from " + name + " where 1 = 0");
117 ResultSet set = select.executeQuery();
118 ResultSetMetaData metaData = set.getMetaData();
119
120 for (int i = 1; i <= metaData.getColumnCount(); ++i) {
121 String columnName = metaData.getColumnName(i);
122 int dataType = metaData.getColumnType(i);
123 long precision = metaData.getPrecision(i);
124 int nullable = metaData.isNullable(i);
125 String typeName = metaData.getColumnTypeName(i);
126 Type type = _mapper.getType(dataType, precision);
127 if (type == null) {
128
129
130
131
132 type = _types.getNearestType(dataType, precision);
133 if (type == null) {
134
135
136 throw new InvalidTypeException(
137 "JDBC driver error. Type=" + dataType
138 + ", precision=" + precision + "(SQL type="
139 + typeName + ") isn't supported by "
140 + "Connection.getMetaData().getTypeInfo(), "
141 + "but is referred to by "
142 + "Connection.getMetaData().getColumns()");
143 }
144 }
145
146 Attribute attribute = new Attribute();
147 attribute.setName(columnName);
148 attribute.setType(type.getSymbolicType());
149 if (nullable == DatabaseMetaData.columnNoNulls) {
150 attribute.setNotNull(true);
151 } else {
152 attribute.setNotNull(false);
153 }
154 result.addAttribute(attribute);
155 }
156 } catch (SQLException exception) {
157 throw new PersistenceException(
158 "Failed to determine the schema of table=" + name,
159 exception);
160 } finally {
161 SQLHelper.close(select);
162 }
163 return result;
164 }
165
166 /***
167 * Returns the {@link Type} for an {@link Attribute}.
168 *
169 * @param attribute the attribute
170 * @return the type
171 * @throws PersistenceException if {@link Attribute#getType} is invalid, or
172 * the RDBMS doesn't support the type
173 */
174 public Type getType(Attribute attribute) throws PersistenceException {
175 Type result = null;
176 Type type = Type.getType(attribute.getType());
177 Type map = _mapper.getType(type.getType(), type.getPrecision());
178 if (map == null) {
179 throw new PersistenceException(
180 "Database does not support type=" + attribute.getType());
181 }
182
183 if (type.getType() != map.getType()) {
184 result = map;
185 } else {
186 boolean parameters = type.getParameters();
187 long precision = type.getPrecision();
188 if (precision <= map.getPrecision()) {
189 if (precision == -1) {
190 precision = map.getPrecision();
191 parameters = map.getParameters();
192 }
193 result = new Type(map.getType(), map.getName(),
194 precision, parameters);
195 } else {
196 throw new PersistenceException(
197 attribute.getName() + type + " exceeds precision for "
198 + map + " precision=" + map.getPrecision());
199 }
200 }
201 return result;
202 }
203
204 /***
205 * Returns true if a table exists.
206 *
207 * @param table the name of the table
208 */
209 public boolean getTableExists(String table) throws PersistenceException {
210 boolean result = false;
211
212 String name = table;
213 ResultSet set = null;
214 try {
215 DatabaseMetaData metaData = _connection.getMetaData();
216 if (metaData.storesUpperCaseIdentifiers()) {
217 name = name.toUpperCase();
218 }
219
220 set = metaData.getTables(_connection.getCatalog(), null,
221 name, null);
222 if (set.next()) {
223 result = true;
224 }
225 } catch (SQLException exception) {
226 throw new PersistenceException("Failed to determine if table="
227 + table + " exists", exception);
228 } finally {
229 SQLHelper.close(set);
230 }
231 return result;
232 }
233
234 }