001: /*
002: * sqlc 1
003: * SQL Compiler
004: * Copyright (C) 2003 Hammurapi Group
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2 of the License, or (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * URL: http://www.hammurapi.biz/products/sqlc/index.html
021: * e-Mail: support@hammurapi.biz
022: */
023: package biz.hammurapi.sql.metadata;
024:
025: import java.io.File;
026: import java.io.FileInputStream;
027: import java.io.FileOutputStream;
028: import java.io.IOException;
029: import java.io.ObjectInputStream;
030: import java.io.ObjectOutputStream;
031: import java.io.Serializable;
032: import java.sql.Connection;
033: import java.sql.DatabaseMetaData;
034: import java.sql.DriverManager;
035: import java.sql.ResultSet;
036: import java.sql.SQLException;
037: import java.util.ArrayList;
038: import java.util.Collection;
039: import java.util.Collections;
040: import java.util.HashMap;
041: import java.util.Iterator;
042: import java.util.Map;
043: import java.util.zip.GZIPInputStream;
044: import java.util.zip.GZIPOutputStream;
045:
046: import org.apache.commons.cli.CommandLine;
047: import org.apache.commons.cli.CommandLineParser;
048: import org.apache.commons.cli.HelpFormatter;
049: import org.apache.commons.cli.Option;
050: import org.apache.commons.cli.OptionBuilder;
051: import org.apache.commons.cli.Options;
052: import org.apache.commons.cli.ParseException;
053: import org.apache.commons.cli.PosixParser;
054:
055: import biz.hammurapi.sql.SQLProcessor;
056: import biz.hammurapi.util.Visitable;
057: import biz.hammurapi.util.VisitableBase;
058: import biz.hammurapi.util.Visitor;
059:
060: /**
061: * @author Pavel Vlasov
062: * @version $Revision: 1.8 $
063: */
064: public class Metadata extends VisitableBase implements Serializable {
065: /**
066: * Comment for <code>serialVersionUID</code>
067: */
068: private static final long serialVersionUID = 3594667669956221889L;
069: private Map descriptors = new HashMap();
070: private String engineType;
071:
072: public static Object toKey(String catalog, String schema,
073: String table) {
074: Collection ret = new ArrayList();
075: ret.add(catalog);
076: ret.add(schema);
077: ret.add(table);
078: return ret;
079: }
080:
081: public static interface TableAcceptor {
082: boolean accept(String catalog, String schema, String table);
083: }
084:
085: private Map catalogs = new HashMap();
086:
087: private Catalog getCatalog(String name) {
088: Catalog ret = (Catalog) catalogs.get(name);
089: if (ret == null) {
090: ret = new Catalog(name);
091: catalogs.put(name, ret);
092: }
093: return ret;
094: }
095:
096: public Collection getCatalogs() {
097: return catalogs.values();
098: }
099:
100: public Metadata(SQLProcessor processor, String[] tableTypes,
101: GenerationPolicy policy, TableAcceptor acceptor)
102: throws SQLException {
103: engineType = policy.generateEngineType();
104: Connection connection = processor.getConnection();
105: try {
106: DatabaseMetaData metaData = connection.getMetaData();
107: ResultSet tables = metaData.getTables(null, null, null,
108: tableTypes);
109: try {
110: while (tables.next()) {
111: String catalog = tables.getString("TABLE_CAT");
112: String schema = tables.getString("TABLE_SCHEM");
113: String tableName = tables.getString("TABLE_NAME");
114:
115: if (acceptor == null
116: || acceptor.accept(catalog, schema,
117: tableName)) {
118: TableDescriptor descriptor = new TableDescriptor(
119: this );
120: descriptor.setCatalog(catalog);
121: descriptor.setSchema(schema);
122: descriptor.setTableName(tableName);
123:
124: descriptor.setEntityType(policy
125: .generateEntityType(descriptor
126: .getCatalog(), descriptor
127: .getSchema(), descriptor
128: .getTableName()));
129: descriptor.setFactoryType(policy
130: .generateFactoryType(descriptor
131: .getCatalog(), descriptor
132: .getSchema(), descriptor
133: .getTableName()));
134: descriptor.setPkType(policy.generatePKType(
135: descriptor.getCatalog(), descriptor
136: .getSchema(), descriptor
137: .getTableName()));
138: descriptor.setValueType(policy
139: .generateValueType(descriptor
140: .getCatalog(), descriptor
141: .getSchema(), descriptor
142: .getTableName()));
143:
144: descriptor.setEntityImplType(policy
145: .generateEntityImplType(descriptor
146: .getCatalog(), descriptor
147: .getSchema(), descriptor
148: .getTableName()));
149: descriptor.setFactoryImplType(policy
150: .generateFactoryImplType(descriptor
151: .getCatalog(), descriptor
152: .getSchema(), descriptor
153: .getTableName()));
154: descriptor.setPkImplType(policy
155: .generatePKImplType(descriptor
156: .getCatalog(), descriptor
157: .getSchema(), descriptor
158: .getTableName()));
159: descriptor.setValueImplType(policy
160: .generateValueImplType(descriptor
161: .getCatalog(), descriptor
162: .getSchema(), descriptor
163: .getTableName()));
164:
165: descriptors.put(toKey(descriptor.getCatalog(),
166: descriptor.getSchema(), descriptor
167: .getTableName()), descriptor);
168: getCatalog(descriptor.getCatalog()).getSchema(
169: descriptor.getSchema())
170: .addTableDescriptor(descriptor);
171:
172: loadColumns(policy, metaData, descriptor);
173: loadPrimaryKey(metaData, descriptor);
174: loadImportedKeys(policy, metaData, descriptor);
175: loadExportedKeys(policy, metaData, descriptor);
176:
177: loadIndices(policy, metaData, descriptor);
178: }
179: }
180: } finally {
181: tables.close();
182: }
183: } finally {
184: processor.releaseConnection(connection);
185: }
186: }
187:
188: /**
189: * @param policy
190: * @param metaData
191: * @param descriptor
192: * @throws SQLException
193: */
194: private void loadColumns(GenerationPolicy policy,
195: DatabaseMetaData metaData, TableDescriptor descriptor) {
196: try {
197: ResultSet columns = metaData.getColumns(descriptor
198: .getCatalog(), descriptor.getSchema(), descriptor
199: .getTableName(), null);
200: try {
201: while (columns.next()) {
202: ColumnDescriptor columnDescriptor = new ColumnDescriptor();
203: columnDescriptor.setDbName(columns
204: .getString("COLUMN_NAME"));
205: columnDescriptor.setDbType(columns
206: .getInt("DATA_TYPE"));
207: columnDescriptor
208: .setName(descriptor
209: .fixJavaName(policy
210: .generateColumnName(columnDescriptor
211: .getDbName())));
212: columnDescriptor.setLabel(policy
213: .generateLabel(columnDescriptor.getName()));
214: columnDescriptor.setJavaType(policy
215: .getJavaType(columnDescriptor.getDbType()));
216: columnDescriptor.setPosition(columns
217: .getInt("ORDINAL_POSITION"));
218: columnDescriptor
219: .setNullable(columns.getInt("NULLABLE") != DatabaseMetaData.columnNoNulls);
220: columnDescriptor.setDbTypeName(columns
221: .getString("TYPE_NAME"));
222: columnDescriptor.setDecimalDigits((Number) columns
223: .getObject("DECIMAL_DIGITS"));
224: columnDescriptor.setSize((Number) columns
225: .getObject("COLUMN_SIZE"));
226: descriptor.add(columnDescriptor);
227: }
228: } finally {
229: columns.close();
230: }
231: } catch (SQLException e) {
232: warnings.add("Could not load columns for table "
233: + descriptor.toString() + ": " + e);
234: }
235: }
236:
237: /**
238: * @param metaData
239: * @param descriptor
240: * @throws SQLException
241: */
242: private void loadPrimaryKey(DatabaseMetaData metaData,
243: TableDescriptor descriptor) {
244: try {
245: ResultSet primaryKeys = metaData.getPrimaryKeys(descriptor
246: .getCatalog(), descriptor.getSchema(), descriptor
247: .getTableName());
248: try {
249: while (primaryKeys.next()) {
250: KeyEntry ke = new KeyEntry(descriptor);
251: ke.setColumnName(primaryKeys
252: .getString("COLUMN_NAME"));
253: ke.setColumnSequence(primaryKeys.getInt("KEY_SEQ"));
254: descriptor.getPrimaryKey().add(ke);
255: ((ColumnDescriptor) descriptor.columnDescriptorsMap
256: .get(ke.getColumnName()))
257: .setPrimaryKey(true);
258: // System.out.println(ke.getName());
259: }
260: } finally {
261: primaryKeys.close();
262: }
263:
264: if (descriptor.getPrimaryKey().size() == 1) {
265: descriptor.setPkType(((KeyEntry) descriptor
266: .getPrimaryKey().iterator().next()).getType());
267: }
268: } catch (SQLException e) {
269: warnings.add("Could not load primary key for table "
270: + descriptor.toString() + ": " + e);
271: }
272: }
273:
274: /**
275: * @param policy
276: * @param metaData
277: * @param descriptor
278: * @throws SQLException
279: */
280: private void loadImportedKeys(GenerationPolicy policy,
281: DatabaseMetaData metaData, TableDescriptor descriptor) {
282: try {
283: ResultSet importedKeys = metaData.getImportedKeys(
284: descriptor.getCatalog(), descriptor.getSchema(),
285: descriptor.getTableName());
286: try {
287: while (importedKeys.next()) {
288: String keyName = importedKeys.getString("FK_NAME");
289: if (keyName == null) {
290: keyName = "FK_"
291: + importedKeys
292: .getString("FKTABLE_NAME")
293: + "_"
294: + importedKeys
295: .getString("PKTABLE_NAME");
296: }
297:
298: KeyDescriptor ikd = (KeyDescriptor) descriptor
299: .getImportedKeys().get(keyName);
300: if (ikd == null) {
301: ikd = new KeyDescriptor(this );
302: ikd.setDbName(keyName);
303: ikd.setName(policy.generateKeyName(keyName));
304:
305: ikd.setFkCatalog(importedKeys
306: .getString("FKTABLE_CAT"));
307: ikd.setFkSchema(importedKeys
308: .getString("FKTABLE_SCHEM"));
309: ikd.setFkTable(importedKeys
310: .getString("FKTABLE_NAME"));
311:
312: ikd.setPkCatalog(importedKeys
313: .getString("PKTABLE_CAT"));
314: ikd.setPkSchema(importedKeys
315: .getString("PKTABLE_SCHEM"));
316: ikd.setPkTable(importedKeys
317: .getString("PKTABLE_NAME"));
318:
319: ikd.setDeleteRule(importedKeys
320: .getShort("DELETE_RULE"));
321: ikd.setUpdateRule(importedKeys
322: .getShort("UPDATE_RULE"));
323:
324: descriptor.addImportedKey(ikd);
325: }
326:
327: KeyEntry ke = new KeyEntry(descriptor);
328: ke.setColumnName(importedKeys
329: .getString("FKCOLUMN_NAME"));
330: ke
331: .setColumnSequence(importedKeys
332: .getInt("KEY_SEQ"));
333: ikd.getColumns().add(ke);
334: ((ColumnDescriptor) descriptor.columnDescriptorsMap
335: .get(ke.getColumnName()))
336: .setForeignKey(true);
337: }
338: } finally {
339: importedKeys.close();
340: }
341:
342: Iterator ikit = descriptor.getImportedKeys().values()
343: .iterator();
344: while (ikit.hasNext()) {
345: Collections.sort(((KeyDescriptor) ikit.next())
346: .getColumns());
347: }
348: } catch (SQLException e) {
349: warnings.add("Could not load imported keys for table "
350: + descriptor.toString() + ": " + e);
351: }
352: }
353:
354: /**
355: * @param policy
356: * @param metaData
357: * @param descriptor
358: * @throws SQLException
359: */
360: private void loadExportedKeys(GenerationPolicy policy,
361: DatabaseMetaData metaData, TableDescriptor descriptor) {
362: try {
363: ResultSet exportedKeys = metaData.getExportedKeys(
364: descriptor.getCatalog(), descriptor.getSchema(),
365: descriptor.getTableName());
366: try {
367: while (exportedKeys.next()) {
368: String keyName = exportedKeys.getString("FK_NAME");
369: if (keyName == null) {
370: keyName = "FK_"
371: + exportedKeys
372: .getString("FKTABLE_NAME")
373: + "_"
374: + exportedKeys
375: .getString("PKTABLE_NAME");
376: }
377:
378: KeyDescriptor ekd = (KeyDescriptor) descriptor
379: .getExportedKeys().get(keyName);
380: if (ekd == null) {
381: ekd = new KeyDescriptor(this );
382: ekd.setDbName(keyName);
383: ekd.setName(policy.generateKeyName(keyName));
384:
385: ekd.setFkCatalog(exportedKeys
386: .getString("FKTABLE_CAT"));
387: ekd.setFkSchema(exportedKeys
388: .getString("FKTABLE_SCHEM"));
389: ekd.setFkTable(exportedKeys
390: .getString("FKTABLE_NAME"));
391:
392: ekd.setPkCatalog(exportedKeys
393: .getString("PKTABLE_CAT"));
394: ekd.setPkSchema(exportedKeys
395: .getString("PKTABLE_SCHEM"));
396: ekd.setPkTable(exportedKeys
397: .getString("PKTABLE_NAME"));
398:
399: ekd.setDeleteRule(exportedKeys
400: .getShort("DELETE_RULE"));
401: ekd.setUpdateRule(exportedKeys
402: .getShort("UPDATE_RULE"));
403:
404: descriptor.addExportedKey(ekd);
405: }
406:
407: KeyEntry ke = new KeyEntry(descriptor);
408: ke.setColumnName(exportedKeys
409: .getString("FKCOLUMN_NAME"));
410: ke
411: .setColumnSequence(exportedKeys
412: .getInt("KEY_SEQ"));
413: ekd.getColumns().add(ke);
414: }
415: } finally {
416: exportedKeys.close();
417: }
418:
419: Iterator ekit = descriptor.getExportedKeys().values()
420: .iterator();
421: while (ekit.hasNext()) {
422: Collections.sort(((KeyDescriptor) ekit.next())
423: .getColumns());
424: }
425: } catch (SQLException e) {
426: warnings.add("Could not load exported keys for table "
427: + descriptor.toString() + ": " + e);
428: }
429: }
430:
431: /**
432: * @param policy
433: * @param metaData
434: * @param descriptor
435: * @throws SQLException
436: */
437: private void loadIndices(GenerationPolicy policy,
438: DatabaseMetaData metaData, TableDescriptor descriptor) {
439: try {
440: ResultSet indices = metaData.getIndexInfo(descriptor
441: .getCatalog(), descriptor.getSchema(), descriptor
442: .getTableName(), false, true);
443: try {
444: while (indices.next()) {
445: String indexName = indices.getString("INDEX_NAME");
446:
447: IndexDescriptor id = (IndexDescriptor) descriptor.indices
448: .get(indexName);
449: if (id == null) {
450: id = new IndexDescriptor(descriptor);
451: id.setName(indexName);
452: id.setInfo(policy.generateIndexInfo(indexName,
453: descriptor));
454: id.setUnique(!indices.getBoolean("NON_UNIQUE"));
455:
456: descriptor.addIndex(id);
457: }
458:
459: KeyEntry ke = new KeyEntry(descriptor);
460: ke.setColumnName(indices.getString("COLUMN_NAME"));
461: ke.setColumnSequence(indices
462: .getInt("ORDINAL_POSITION"));
463: ke.setDescending("D".equalsIgnoreCase(indices
464: .getString("ASC_OR_DESC")));
465: id.getColumns().add(ke);
466: }
467: } finally {
468: indices.close();
469: }
470:
471: Iterator iit = descriptor.getIndices().iterator();
472: while (iit.hasNext()) {
473: Collections.sort(((IndexDescriptor) iit.next())
474: .getColumns());
475: }
476: } catch (SQLException e) {
477: warnings.add("Could not load indices for table "
478: + descriptor.toString() + ": " + e);
479: }
480: }
481:
482: public TableDescriptor getTableDescriptor(String catalog,
483: String schema, String table) {
484: return (TableDescriptor) descriptors.get(toKey(catalog, schema,
485: table));
486: }
487:
488: public Collection getTableDescriptors() {
489: return descriptors.values();
490: }
491:
492: public String getEngineType() {
493: return engineType;
494: }
495:
496: public void acceptChildren(Visitor visitor) {
497: Iterator it = getCatalogs().iterator();
498: while (it.hasNext()) {
499: ((Visitable) it.next()).accept(visitor);
500: }
501: }
502:
503: private static void printHelpAndExit(Options options) {
504: HelpFormatter formatter = new HelpFormatter();
505: formatter.printHelp(
506: "Usage: java [java options] "
507: + Metadata.class.getName()
508: + " [options] <output file>", options, false);
509: System.exit(1);
510: }
511:
512: /**
513: * Loads metadata from MDZ file
514: * @param file
515: * @return
516: * @throws IOException
517: * @throws ClassNotFoundException
518: */
519: public static Metadata load(File file) throws IOException,
520: ClassNotFoundException {
521: ObjectInputStream ois = new ObjectInputStream(
522: new GZIPInputStream(new FileInputStream(file)));
523: try {
524: return (Metadata) ois.readObject();
525: } finally {
526: ois.close();
527: }
528: }
529:
530: /**
531: * Saves metadata to file
532: * @param args
533: */
534: public static void main(String[] args) {
535: Options options = new Options();
536:
537: Option driverOption = OptionBuilder.withArgName("class name")
538: .hasArg().withDescription("Driver class").isRequired(
539: true).create("d");
540:
541: options.addOption(driverOption);
542:
543: Option urlOption = OptionBuilder.withArgName("URL").hasArg()
544: .withDescription("Connection URL").isRequired(true)
545: .create("u");
546:
547: options.addOption(urlOption);
548:
549: Option userOption = OptionBuilder.withArgName("user").hasArg()
550: .withDescription("Database user").isRequired(false)
551: .create("U");
552:
553: options.addOption(userOption);
554:
555: Option passwordOption = OptionBuilder.withArgName("password")
556: .hasArg().withDescription("Database password")
557: .isRequired(false).create("p");
558:
559: options.addOption(passwordOption);
560:
561: Option qualifiedOption = OptionBuilder.withDescription(
562: "Produce flat table names (without catalog and schema")
563: .isRequired(false).create("f");
564:
565: options.addOption(qualifiedOption);
566:
567: Option helpOption = OptionBuilder.withDescription(
568: "Print this message").isRequired(false).create("h");
569: options.addOption(helpOption);
570:
571: CommandLineParser parser = new PosixParser();
572: CommandLine line = null;
573: try {
574: line = parser.parse(options, args);
575: } catch (ParseException e) {
576: System.err.println(e.getMessage());
577: System.err.flush();
578: printHelpAndExit(options);
579: }
580:
581: if (line.getArgs().length != 1) {
582: printHelpAndExit(options);
583: }
584:
585: if (line.hasOption("h")) {
586: printHelpAndExit(options);
587: }
588:
589: try {
590: Class.forName(line.getOptionValue('d'));
591: Connection con = DriverManager.getConnection(line
592: .getOptionValue('u'), line.getOptionValue('U'),
593: line.getOptionValue('p'));
594:
595: GenerationPolicy generationPolicy = line.hasOption('f') ? new FlatGenerationPolicy()
596: : new DefaultGenerationPolicy();
597: Metadata metadata = new Metadata(
598: new SQLProcessor(con, null), new String[] {
599: "TABLE", "VIEW" }, generationPolicy, null);
600: ObjectOutputStream oos = new ObjectOutputStream(
601: new GZIPOutputStream(new FileOutputStream(line
602: .getArgs()[0])));
603: oos.writeObject(metadata);
604: oos.close();
605: } catch (Exception e) {
606: e.printStackTrace();
607: }
608: }
609:
610: private Collection warnings = new ArrayList();
611:
612: public Collection getWarnings() {
613: return warnings;
614: }
615: }
|