1 package com.quantum.model;
3 import java.sql.Connection;
4 import java.sql.DatabaseMetaData;
5 import java.sql.ResultSet;
6 import java.sql.SQLException;
7 import java.util.ArrayList;
8 import java.util.Collection;
9 import java.util.Collections;
10 import java.util.HashSet;
11 import java.util.List;
14 import com.quantum.adapters.DatabaseAdapter;
15 import com.quantum.sql.MultiSQLServer;
16 import com.quantum.sql.SQLResultSetResults;
21 public class Database {
23 private static final int TABLE_METADATA_TABLE_SCHEM = 2;
24 private static final int TABLE_METADATA_TABLE_NAME = 3;
26 private static final int TABLE_TYPE_METADATA_TABLE_TYPE = 1;
28 private static final int FOREIGN_KEY_METADATA_PKTABLE_SCHEM = 2;
29 private static final int FOREIGN_KEY_METADATA_PKTABLE_NAME = 3;
30 private static final int FOREIGN_KEY_METADATA_PKCOLUMN_NAME = 4;
31 private static final int FOREIGN_KEY_METADATA_FKTABLE_SCHEM = 6;
32 private static final int FOREIGN_KEY_METADATA_FKTABLE_NAME = 7;
33 private static final int FOREIGN_KEY_METADATA_FKCOLUMN_NAME = 8;
34 private static final int FOREIGN_KEY_METADATA_KEY_SEQ = 9;
35 private static final int FOREIGN_KEY_METADATA_DELETE_RULE = 11;
36 private static final int FOREIGN_KEY_METADATA_FK_NAME = 12;
38 private static final int TYPE_INFO_METADATA_TYPE_NAME = 1;
39 private static final int TYPE_INFO_METADATA_DATA_TYPE = 2;
40 private static final int TYPE_INFO_METADATA_PRECISION = 3;
41 private static final int TYPE_INFO_METADATA_LITERAL_PREFIX = 4;
42 private static final int TYPE_INFO_METADATA_LITERAL_SUFFIX = 5;
43 private static final int TYPE_INFO_METADATA_CREATE_PARMS = 6;
45 private DatabaseAdapter databaseAdapter;
46 private Bookmark bookmark;
48 private List entityTypes;
50 public Database(Bookmark bookmark) {
51 this.bookmark = bookmark;
52 this.databaseAdapter = bookmark.getAdapter();
55 private static final String[] ALL_TABLE_TYPES = {
58 Entity.SEQUENCE_TYPE };
60 private static final List STANDARD_TABLE_TYPES =
61 Collections.synchronizedList(new ArrayList());
64 for (int i = 0, length = (ALL_TABLE_TYPES == null) ? 0 : ALL_TABLE_TYPES.length;
67 STANDARD_TABLE_TYPES.add(ALL_TABLE_TYPES[i]);
71 public synchronized String[] getEntityTypes()
72 throws NotConnectedException, SQLException {
73 if (this.entityTypes == null) {
74 Collection collection = initializeEntityTypes(this.bookmark.getConnection());
75 this.entityTypes = Collections.synchronizedList(new ArrayList(collection));
77 return (String[]) this.entityTypes.toArray(new String[this.entityTypes.size()]);
80 public String getUsername() throws NotConnectedException, SQLException {
81 return getMetaData().getUserName();
86 * <p>This method returns a set of entity types supported by the database
87 * adapter. This list will always be limited to Tables, Views and
90 * <p>Not all databases support all types. MySQL only supports
91 * Tables. Informix supports Tables and Views. Oracle and DB2 support
92 * Tables, Views and Sequences.</p>
95 * @return a set of Strings, typically "TABLE", "VIEW", and "SEQUENCE"
96 * @throws SQLException
98 private Set initializeEntityTypes(Connection connection) throws SQLException {
100 Set set = new HashSet();
101 if (this.databaseAdapter.getShowTableQuery(this.bookmark.getUsername()) != null) {
102 set.add(Entity.TABLE_TYPE);
103 } else if (this.databaseAdapter.getShowViewQuery(this.bookmark.getUsername()) != null) {
104 set.add(Entity.VIEW_TYPE);
105 } else if (this.databaseAdapter.getShowSequenceQuery(this.bookmark.getUsername()) != null) {
106 set.add(Entity.SEQUENCE_TYPE);
109 DatabaseMetaData metaData = connection.getMetaData();
110 ResultSet resultSet = metaData.getTableTypes();
111 while (resultSet.next()) {
112 String type = resultSet.getString(TABLE_TYPE_METADATA_TABLE_TYPE);
114 // Informix, in particular, pads this with extra spaces
117 if (STANDARD_TABLE_TYPES.contains(type)) {
124 public String getInformation() throws SQLException {
126 DatabaseMetaData metaData = getMetaData();
128 return metaData == null ? null : metaData.getDatabaseProductName() + " "
129 + metaData.getDatabaseProductVersion();
130 } catch (NotConnectedException e) {
131 // TODO: think about this...
137 * Get a list of entities (tables, views, sequences) for a particular
138 * bookmark. This function is usually not redefined because it gives
139 * an external interface. You will usually redefine the getShowTableQuery(),
140 * getShowViewQuery(), etc.
143 * the bookmark that describes the database that is being accessed.
144 * @param passwordFinder -
145 * a utility class that knows how to obtain a password, if required
147 * the schema from which to extract
149 * the type ("VIEW", "TABLE", etc.) of entities to extract or null
150 * if all entity types should be extracted
152 * an array of entity objects representing the tables, views and sequences.
153 * @throws SQLException
155 public Entity[] getEntities(Bookmark bookmark, Schema schema, String type)
156 throws SQLException, NotConnectedException {
157 Connection connection = bookmark.getConnection();
158 Entity[] result = getEntities(bookmark, connection, schema, type);
159 return (result == null) ? new Entity[0] : result;
162 protected Entity[] getEntities(Bookmark bookmark, Connection connection, Schema schema, String type)
163 throws SQLException {
165 List list = new ArrayList();
166 String[] types = (type == null) ? ALL_TABLE_TYPES : new String[] { type };
168 for (int i = 0; i < types.length; i++) {
169 list.addAll(getEntitiesList(bookmark, connection, types[i], schema));
172 return (Entity[]) list.toArray(new Entity[list.size()]);
175 protected List getEntitiesList(Bookmark bookmark, Connection connection, String type, Schema schema)
176 throws SQLException {
178 List list = new ArrayList();
179 // We try first the JDBC driver
180 DatabaseMetaData metaData = connection.getMetaData();
181 // getTables needs a null schema to get all the schemas. So we don't pass a "" schema, but a null one
182 ResultSet set = null;
183 if (metaData.supportsSchemasInTableDefinitions()) {
184 set = metaData.getTables(null, (schema != null) ? schema.getName() : null, "%", new String[] { type });
186 set = metaData.getTables(null, null, "%", new String[] { type });
190 String tempSchema = set.getString(TABLE_METADATA_TABLE_SCHEM);
191 tempSchema = (tempSchema == null) ? "" : tempSchema.trim();
192 String tableName = set.getString(TABLE_METADATA_TABLE_NAME);
193 tableName = (tableName == null) ? "" : tableName.trim();
195 if (tableName != null && tableName.length() > 0) {
196 Entity entity = EntityFactory.getInstance().create(bookmark, tempSchema, tableName, type);
197 if (entity != null) {
203 // If we have some results, then the JDBC driver is working,
204 // so we return the results and quit
209 // If no results, we check also the sql query to get the list of entities
210 SQLResultSetResults results = null;
211 // Get the proper sql query to the appropiate type of entity
212 String sql = getSQL(bookmark, type, schema);
213 // If nothing returned, too bad, it seems there is no sql query for that database and entity type
215 results = (SQLResultSetResults) MultiSQLServer.getInstance().execute(
216 bookmark, connection, sql, Integer.MAX_VALUE);
217 for (int i = 1, size = (results == null) ? 0 : results.getRowCount(); i <= size; i++) {
218 String schemaName = results.getColumnCount() == 1
219 ? schema.getName() : results.getElement(1, i).toString();
220 if (schemaName != null) {
221 schemaName = schemaName.trim();
223 String tableName = results.getColumnCount() == 1
224 ? results.getElement(1, i).toString()
225 : results.getElement(2, i).toString();
226 if (tableName != null && tableName.length() > 0) {
227 Entity entity = EntityFactory.getInstance().create(
228 bookmark, schemaName, tableName, type);
229 if (entity != null) {
238 public DataType[] getTypes() throws NotConnectedException, SQLException {
239 DatabaseMetaData metaData = getMetaData();
240 List list = new ArrayList();
241 ResultSet results = metaData.getTypeInfo();
243 while (results.next()) {
244 list.add(new DataType(
245 results.getInt(TYPE_INFO_METADATA_DATA_TYPE),
246 results.getString(TYPE_INFO_METADATA_TYPE_NAME),
247 results.getInt(TYPE_INFO_METADATA_PRECISION),
248 results.getString(TYPE_INFO_METADATA_LITERAL_PREFIX),
249 results.getString(TYPE_INFO_METADATA_LITERAL_SUFFIX),
250 results.getString(TYPE_INFO_METADATA_CREATE_PARMS)
256 return (DataType[]) list.toArray(new DataType[list.size()]);
262 * @throws NotConnectedException
263 * @throws SQLException
265 private DatabaseMetaData getMetaData() throws NotConnectedException, SQLException {
266 Connection connection = this.bookmark.getConnection();
267 DatabaseMetaData metaData = connection.getMetaData();
271 private String getSQL(Bookmark bookmark, String type, Schema schema) {
272 if (Entity.TABLE_TYPE.equals(type)) {
273 return this.databaseAdapter.getShowTableQuery(schema.getName());
274 } else if (Entity.VIEW_TYPE.equals(type)) {
275 return this.databaseAdapter.getShowViewQuery(schema.getName());
276 } else if (Entity.SEQUENCE_TYPE.equals(type)) {
277 return this.databaseAdapter.getShowSequenceQuery(schema.getName());
283 public ForeignKey[] getExportedKeys(String schema, String entityName)
284 throws NotConnectedException, SQLException {
285 DatabaseMetaData metaData = getMetaData();
286 List list = new ArrayList();
287 return getForeignKeys(list, metaData.getExportedKeys(null, schema, entityName));
290 public ForeignKey[] getImportedKeys(String schema, String entityName)
291 throws NotConnectedException, SQLException {
292 DatabaseMetaData metaData = getMetaData();
293 List list = new ArrayList();
294 return getForeignKeys(list, metaData.getImportedKeys(null, schema, entityName));
301 * @throws SQLException
303 private ForeignKey[] getForeignKeys(List list, ResultSet resultSet) throws SQLException {
304 ForeignKeyImpl foreignKey = null;
306 int lowestKeySequence = Integer.MAX_VALUE;
308 while (resultSet.next()) {
309 int keySequence = resultSet.getInt(FOREIGN_KEY_METADATA_KEY_SEQ);
310 lowestKeySequence = Math.min(lowestKeySequence, keySequence);
312 if (keySequence == lowestKeySequence || foreignKey == null) {
313 foreignKey = new ForeignKeyImpl();
314 list.add(foreignKey);
315 foreignKey.setName(resultSet.getString(FOREIGN_KEY_METADATA_FK_NAME));
316 foreignKey.setDeleteRule(resultSet.getShort(FOREIGN_KEY_METADATA_DELETE_RULE));
317 foreignKey.setForeignEntitySchema(resultSet.getString(FOREIGN_KEY_METADATA_FKTABLE_SCHEM));
318 foreignKey.setForeignEntityName(resultSet.getString(FOREIGN_KEY_METADATA_FKTABLE_NAME));
319 foreignKey.setLocalEntitySchema(resultSet.getString(FOREIGN_KEY_METADATA_PKTABLE_SCHEM));
320 foreignKey.setLocalEntityName(resultSet.getString(FOREIGN_KEY_METADATA_PKTABLE_NAME));
323 foreignKey.addColumns(
324 resultSet.getString(FOREIGN_KEY_METADATA_PKCOLUMN_NAME),
325 resultSet.getString(FOREIGN_KEY_METADATA_FKCOLUMN_NAME));
327 return (ForeignKey[]) list.toArray(new ForeignKey[list.size()]);
335 * @throws SQLException
336 * @throws NotConnectedException
338 public Schema[] getSchemas() throws NotConnectedException, SQLException {
339 DatabaseMetaData metaData = getMetaData();
340 List list = new ArrayList();
342 if (metaData.supportsSchemasInTableDefinitions()) {
343 ResultSet resultSet = metaData.getSchemas();
345 while (resultSet.next()) {
346 String schemaName = resultSet.getString("TABLE_SCHEM");
347 list.add(new Schema(schemaName));
353 return (Schema[]) list.toArray(new Schema[list.size()]);