001: /**
002: * Sequoia: Database clustering technology.
003: * Copyright (C) 2002-2004 French National Institute For Research In Computer
004: * Science And Control (INRIA).
005: * Copyright (C) 2005 AmicoSoft, Inc. dba Emic Networks
006: * Copyright (C) 2005 Continuent, Inc.
007: * Contact: sequoia@continuent.org
008: *
009: * Licensed under the Apache License, Version 2.0 (the "License");
010: * you may not use this file except in compliance with the License.
011: * You may obtain a copy of the License at
012: *
013: * http://www.apache.org/licenses/LICENSE-2.0
014: *
015: * Unless required by applicable law or agreed to in writing, software
016: * distributed under the License is distributed on an "AS IS" BASIS,
017: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018: * See the License for the specific language governing permissions and
019: * limitations under the License.
020: *
021: * Initial developer(s): Nicolas Modrzyk.
022: * Contributor(s): Emmanuel Cecchet, Edward Archibald.
023: */package org.continuent.sequoia.controller.backend;
024:
025: import java.sql.Connection;
026: import java.sql.DatabaseMetaData;
027: import java.sql.PreparedStatement;
028: import java.sql.ResultSet;
029: import java.sql.SQLException;
030:
031: import org.continuent.sequoia.common.i18n.Translate;
032: import org.continuent.sequoia.common.log.Trace;
033: import org.continuent.sequoia.common.sql.schema.DatabaseColumn;
034: import org.continuent.sequoia.common.sql.schema.DatabaseProcedure;
035: import org.continuent.sequoia.common.sql.schema.DatabaseProcedureParameter;
036: import org.continuent.sequoia.common.sql.schema.DatabaseProcedureSemantic;
037: import org.continuent.sequoia.common.sql.schema.DatabaseSchema;
038: import org.continuent.sequoia.common.sql.schema.DatabaseTable;
039:
040: /**
041: * This class defines a DatabaseSQLMetaData. It is used to collect metadata from
042: * a live connection to a database
043: *
044: * @author <a href="mailto:ed.archibald@continuent.com">Edward Archibald</a>
045: * @author <a href="mailto:emmanuel.cecchet@continuent.com">Emmanuel Cecchet
046: * </a>
047: * @author <a href="mailto:Nicolas.Modrzyk@inrialpes.fr">Nicolas Modrzyk </a>
048: * @version 1.0
049: */
050: public class DatabaseSQLMetaData {
051: private Trace logger;
052: private Connection connection;
053: private int dynamicPrecision;
054: private boolean gatherSystemTables;
055: private String schemaPattern;
056:
057: /**
058: * Creates a new <code>MetaData</code> object
059: *
060: * @param logger the log4j logger to output to
061: * @param connection a jdbc connection to a database
062: * @param dynamicPrecision precision used to create the schema
063: * @param gatherSystemTables should we gather system tables
064: * @param schemaPattern schema pattern to look for (reduce the scope of
065: * gathering if not null)
066: */
067: public DatabaseSQLMetaData(Trace logger, Connection connection,
068: int dynamicPrecision, boolean gatherSystemTables,
069: String schemaPattern) {
070: super ();
071: this .logger = logger;
072: this .connection = connection;
073: this .dynamicPrecision = dynamicPrecision;
074: this .gatherSystemTables = gatherSystemTables;
075: this .schemaPattern = schemaPattern;
076: }
077:
078: /**
079: * Create a database schema from the given connection
080: *
081: * @param vdbName the virtual database name this schema represents
082: * @return <code>DataSchema</code> contructed from the information collected
083: * through jdbc
084: * @throws SQLException if an error occurs with the given connection
085: */
086: public final DatabaseSchema createDatabaseSchema(String vdbName)
087: throws SQLException {
088: ResultSet rs = null;
089:
090: boolean connectionWasAutocommit = connection.getAutoCommit();
091:
092: if (connectionWasAutocommit)
093: connection.setAutoCommit(false); // Needed for Derby Get DatabaseMetaData
094:
095: DatabaseMetaData metaData = connection.getMetaData();
096: if (metaData == null) {
097: logger.warn(Translate.get("backend.meta.received.null"));
098: return null;
099: }
100:
101: if (logger.isDebugEnabled())
102: logger.debug("Fetching schema with precision '"
103: + DatabaseBackendSchemaConstants
104: .getDynamicSchemaLevel(dynamicPrecision)
105: + (gatherSystemTables ? "'" : "' not")
106: + " including system tables.");
107:
108: DatabaseSchema databaseSchema = new DatabaseSchema(vdbName);
109:
110: // Check if we should get system tables or not
111: String[] types;
112: if (gatherSystemTables) {
113: schemaPattern = null;
114: types = new String[] { "TABLE", "VIEW", "SYSTEM TABLE",
115: "SYSTEM VIEW", "SEQUENCE" };
116: } else
117: types = new String[] { "TABLE", "VIEW", "SEQUENCE" };
118:
119: // Get tables meta data
120: // getTables() gets a description of tables matching the catalog, schema,
121: // table name pattern and type. Sending in null for catalog and schema
122: // drops them from the selection criteria. The table name pattern "%"
123: // means match any substring of 0 or more characters.
124: // Last argument allows to obtain only database tables
125: try {
126: rs = metaData.getTables(null, schemaPattern, "%", types);
127: } catch (Exception e) {
128: // VIEWS cannot be retrieved with this backend
129: logger.error(Translate
130: .get("backend.meta.view.not.supported"), e);
131: if (gatherSystemTables)
132: types = new String[] { "TABLE", "SYSTEM TABLE" };
133: else
134: types = new String[] { "TABLE" };
135: rs = metaData.getTables(null, schemaPattern, "%", types);
136: }
137:
138: if (rs == null) {
139: logger.warn(Translate.get("backend.meta.received.null"));
140: if (connectionWasAutocommit)
141: connection.commit();
142: return null;
143: }
144:
145: // Check if we can get all foreign keys for this database
146: boolean optimized = false;
147: try {
148: optimized = metaData.getExportedKeys(null, null, null) != null;
149: } catch (SQLException ignored) {
150: }
151:
152: String tableName;
153: String schemaName;
154: DatabaseTable table = null;
155: while (rs.next()) {
156: // 1 is table catalog, 2 is table schema, 3 is table name, 4 is type
157: schemaName = rs.getString(2);
158: tableName = rs.getString(3);
159: String type = rs.getString(4);
160: if (logger.isDebugEnabled())
161: logger.debug(Translate.get("backend.meta.found.table",
162: schemaName + "." + tableName));
163:
164: // Create a new table and add it to the database schema
165: table = new DatabaseTable(tableName);
166: table.setSchema(schemaName);
167: if (!type.equals("VIEW") && !optimized) {
168: getExportedKeys(metaData, table);
169: getImportedKeys(metaData, table);
170: }
171: databaseSchema.addTable(table);
172: if (dynamicPrecision >= DatabaseBackendSchemaConstants.DynamicPrecisionColumn) {
173: // Get information about this table columns
174: getColumns(metaData, table);
175: // Get information about this table primary keys
176: if (!type.equals("VIEW")) {
177: getPrimaryKeys(metaData, table);
178: }
179: }
180: }
181:
182: // Get Foreign keys for this database
183: if (optimized)
184: getForeignKeys(metaData, databaseSchema);
185:
186: // Get Procedures for this database
187: if (dynamicPrecision >= DatabaseBackendSchemaConstants.DynamicPrecisionProcedures)
188: getProcedures(metaData, databaseSchema);
189:
190: try {
191: rs.close();
192: } catch (Exception ignore) {
193: }
194:
195: if (connectionWasAutocommit) {
196: try {
197: connection.commit();
198: } catch (Exception ignore) {
199: // This was a read-only transaction
200: }
201:
202: try {
203: // restore connection
204: connection.setAutoCommit(true);
205: } catch (SQLException e1) {
206: // ignore, transaction is no more valid
207: }
208: }
209:
210: return databaseSchema;
211: }
212:
213: /**
214: * @see java.sql.DatabaseMetaData#getProcedures
215: * @see java.sql.DatabaseMetaData#getProcedureColumns
216: */
217: private void getProcedures(DatabaseMetaData metaData,
218: DatabaseSchema schema) {
219: if (logger.isDebugEnabled())
220: logger.debug(Translate.get("backend.meta.get.procedures"));
221:
222: ResultSet rs = null;
223: ResultSet rs2 = null;
224: try {
225: // Get Procedures meta data
226: rs = metaData.getProcedures(null, schemaPattern, "%");
227:
228: if (rs == null) {
229: logger.warn(Translate.get(
230: "backend.meta.get.procedures.failed", metaData
231: .getConnection().getCatalog()));
232: return;
233: }
234:
235: while (rs.next()) {
236: // Each row is a procedure description
237: // 3 = PROCEDURE_NAME
238: // 7 = REMARKS
239: // 8 = PROCEDURE_TYPE
240: DatabaseProcedure procedure = new DatabaseProcedure(rs
241: .getString(3), rs.getString(7), rs.getShort(8));
242:
243: // Check if we need to fetch the procedure parameters
244: if (dynamicPrecision < DatabaseBackendSchemaConstants.DynamicPrecisionProcedures) {
245: if (logger.isDebugEnabled()
246: && schema.getProcedure(procedure.getName()) != null) {
247: logger
248: .debug(Translate
249: .get(
250: "backend.meta.procedure.already.in.schema",
251: procedure.getName()));
252: }
253: continue;
254: }
255:
256: // Get the column information
257: rs2 = metaData.getProcedureColumns(null, null,
258: procedure.getName(), "%");
259: if (rs2 == null)
260: logger.warn(Translate.get(
261: "backend.meta.get.procedure.params.failed",
262: procedure.getName()));
263: else {
264: while (rs2.next()) {
265: // Each row is a parameter description for the current procedure
266: // 4 = COLUMN_NAME
267: // 5 = COLUMN_TYPE
268: // 6 = DATA_TYPE
269: // 7 = TYPE_NAME
270: // 8 = PRECISION
271: // 9 = LENGTH
272: // 10 = SCALE
273: // 11 = RADIX
274: // 12 = NULLABLE
275: // 13 = REMARKS
276: DatabaseProcedureParameter param = new DatabaseProcedureParameter(
277: rs2.getString(4), rs2.getInt(5), rs2
278: .getInt(6), rs2.getString(7),
279: rs2.getFloat(8), rs2.getInt(9), rs2
280: .getInt(10), rs2.getInt(11),
281: rs2.getInt(12), rs2.getString(13));
282: procedure.addParameter(param);
283: if (logger.isDebugEnabled())
284: logger.debug(procedure.getName()
285: + ": adding parameter "
286: + param.getName());
287: }
288: rs2.close();
289: }
290:
291: // Add procedure only if it is not already in schema
292: if (!schema.getProcedures().containsValue(procedure)) {
293: schema.addProcedure(procedure);
294: if (logger.isDebugEnabled())
295: logger.debug(Translate.get(
296: "backend.meta.procedure.added",
297: procedure.getKey()));
298: } else if (logger.isDebugEnabled()) {
299: logger.debug(Translate.get(
300: "backend.meta.procedure.already.in.schema",
301: procedure.getName()));
302: }
303: }
304: } catch (Exception e) {
305: logger.error(Translate.get(
306: "backend.meta.get.procedures.failed", e
307: .getMessage()), e);
308: } finally {
309: try {
310: rs.close();
311: } catch (Exception ignore) {
312: }
313: try {
314: rs2.close();
315: } catch (Exception ignoreAsWell) {
316: }
317: }
318:
319: /*
320: * EXPERIMENTAL: Further initialize the DatabaseProcedure objects to reflect
321: * the semantic information if it exists. For now, just assume that the
322: * tables exist that store the semantic information. Just log a message if
323: * we don't find any semantic information.
324: */
325: getProcedureSemantics(metaData, schema);
326:
327: }
328:
329: /**
330: * This method does a query against the tables which provide the semantic
331: * information for stored procedures. As it processes each row returned, a
332: * lookup is done in the database schema and if the procedure is found, the
333: * information from this query is used to set up the semantics fields in the
334: * DatabaseProcedure object.
335: *
336: * @param metaData reference to a database meta data provider.
337: * @param schema reference to the current database schema.
338: */
339:
340: private void getProcedureSemantics(DatabaseMetaData metaData,
341: DatabaseSchema schema) {
342: ResultSet rs = null;
343: DatabaseProcedure foundProc = null;
344: int paramCount;
345: String objectName = null;
346: String procedureKey = null;
347: PreparedStatement getBaseStmt = null;
348:
349: try {
350: getBaseStmt = connection
351: .prepareStatement("select * from sequoiaSABase where objectType=?");
352: getBaseStmt.setInt(1, 1);
353:
354: /**
355: * Execute the query to get the base semantic information for the
356: * procedures. This call will return a single row for each procedure that
357: * has semantics information.
358: */
359: rs = getBaseStmt.executeQuery();
360: if (rs == null) { // Sanity check but a JDBC compliant driver never returns null
361: logger.warn(Translate.get(
362: "backend.meta.procedure.semantics.get.failed",
363: "Query returned null"));
364: return;
365: }
366:
367: /**
368: * For each row of base semantic information, see if we have the procedure
369: * in the schema already. We should have it.... Then set up the basic
370: * semantics information fields and, finally, call a method that will fill
371: * out all of the tables referenced by recursively calling itself, if
372: * necessary, to follow nested references to other stored procedures.
373: */
374: while (rs.next()) {
375: objectName = rs.getString("objectName");
376: paramCount = rs.getInt("paramCount");
377:
378: procedureKey = DatabaseProcedure.buildKey(objectName,
379: paramCount);
380: if ((foundProc = schema.getProcedure(procedureKey)) == null) {
381: logger.warn(Translate.get(
382: "backend.meta.procedure.notfound",
383: procedureKey));
384: continue;
385: }
386:
387: if (logger.isDebugEnabled())
388: logger.debug(Translate.get(
389: "backend.meta.procedure.found.semantics",
390: procedureKey));
391:
392: /**
393: * set up the base semantics for the DatabaseProcedure.
394: */
395: DatabaseProcedureSemantic procedureSemantics = new DatabaseProcedureSemantic(
396: rs.getBoolean("hasSelect"), rs
397: .getBoolean("hasInsert"), rs
398: .getBoolean("hasUpdate"), rs
399: .getBoolean("hasDelete"), rs
400: .getBoolean("hasDDLWrite"), rs
401: .getBoolean("hasTransaction"), rs
402: .getBoolean("isCausallyDependent"), rs
403: .getBoolean("isCommutative"));
404: foundProc.setSemantic(procedureSemantics);
405:
406: /**
407: * call a method that will set up the tables referenced.
408: */
409: setProcedureTablesReferenced(metaData, schema,
410: objectName, foundProc);
411:
412: }
413: } catch (Exception e) {
414: if (logger.isDebugEnabled())
415: logger.debug(Translate.get(
416: "backend.meta.procedure.semantics.get.failed",
417: e.getMessage()), e);
418: } finally {
419: try {
420: rs.close();
421: } catch (Exception ignore) {
422: }
423: }
424: }
425:
426: private void setProcedureTablesReferenced(
427: DatabaseMetaData metaData, DatabaseSchema schema,
428: String procName, DatabaseProcedure proc) {
429: ResultSet rs = null;
430: int referenceType = 0;
431:
432: PreparedStatement getReferencesStmt = null;
433:
434: try {
435: getReferencesStmt = connection
436: .prepareStatement("select sequoiaSABase.objectName as baseObjectName, sequoiaSAReferences.objectName as referencedObjectName, sequoiaSAReferences.objectType as referencedObjectType,"
437: + " referencedInSelect, referencedInInsert, referencedInUpdate, referencedInDelete, referencedInReplace, referencedInDDLWrite, referencedInDDLRead"
438: + " FROM sequoiaSAReferences, sequoiaSABase"
439: + " WHERE sequoiaSAReferences.baseObjectName = sequoiaSABase.objectName AND sequoiaSAReferences.baseObjectName=?");
440: getReferencesStmt.setString(1, procName);
441:
442: rs = getReferencesStmt.executeQuery();
443:
444: if (rs == null) {
445: if (logger.isDebugEnabled())
446: logger
447: .debug(Translate
448: .get(
449: "backend.meta.procedure.semantics.no.references",
450: procName));
451:
452: getReferencesStmt.close();
453: return;
454: }
455:
456: while (rs.next()) {
457: referenceType = rs.getInt("referencedObjectType");
458:
459: String referencedObjectName = rs
460: .getString("referencedObjectName");
461: if (logger.isDebugEnabled())
462: logger
463: .debug(Translate
464: .get(
465: "backend.meta.procedure.semantics.references.found",
466: new Object[] {
467: proc.getName(),
468: (referenceType == 1) ? "stored procedure"
469: : "table",
470: rs
471: .getString("baseObjectName"),
472: referencedObjectName }));
473:
474: /**
475: * If the reference is a stored procedure, do a recursive call to get
476: * the tables. Otherwise, just add the table to the list.
477: */
478: if (referenceType == 1) {
479: setProcedureTablesReferenced(metaData, schema,
480: referencedObjectName, proc);
481: } else {
482: if (!proc.getSemantic().isReadOnly()) {
483: if (logger.isDebugEnabled())
484: logger
485: .debug(Translate
486: .get(
487: "backend.meta.procedure.semantics.add.reference",
488: new Object[] {
489: proc
490: .getName(),
491: referencedObjectName }));
492:
493: proc.getSemantic().addWriteTable(
494: referencedObjectName);
495: } else
496: logger
497: .error("Invalid semantic detected for stored procedure "
498: + procName
499: + " - Procedure cannot be read-only since it has a non read-only reference "
500: + referencedObjectName);
501: }
502: }
503: } catch (Exception e) {
504: logger
505: .error(
506: Translate
507: .get(
508: "backend.meta.procedures.semantics.set.table.failed",
509: e.getMessage()), e);
510: } finally {
511: try {
512: rs.close();
513: } catch (Exception ignore) {
514: }
515: }
516: }
517:
518: /**
519: * Gets the list of columns of a given database table. The caller must ensure
520: * that the parameters are not <code>null</code>.
521: *
522: * @param metaData the database meta data
523: * @param table the database table
524: * @exception SQLException if an error occurs
525: */
526: private void getColumns(DatabaseMetaData metaData,
527: DatabaseTable table) throws SQLException {
528: ResultSet rs = null;
529: try {
530: // Get columns meta data
531: // getColumns() gets a description of columns matching the catalog,
532: // schema, table name and column name pattern. Sending in null for
533: // catalog and schema drops them from the selection criteria. The
534: // column pattern "%" allows to obtain all columns.
535: rs = metaData.getColumns(null, table.getSchema(), table
536: .getName(), "%");
537:
538: if (rs == null) {
539: logger.warn(Translate.get(
540: "backend.meta.get.columns.failed", table
541: .getSchema()
542: + "." + table.getName()));
543: return;
544: }
545:
546: DatabaseColumn column = null;
547: int type;
548: while (rs.next()) {
549: // 1 is table catalog, 2 is table schema, 3 is table name,
550: // 4 is column name, 5 is data type
551: type = rs.getShort(5);
552: column = new DatabaseColumn(rs.getString(4), false,
553: type);
554: table.addColumn(column);
555:
556: if (logger.isDebugEnabled())
557: logger.debug(Translate.get(
558: "backend.meta.found.column", rs
559: .getString(4)));
560: }
561: } catch (SQLException e) {
562: throw new SQLException(Translate.get(
563: "backend.meta.get.columns.failed", table
564: .getSchema()
565: + "." + table.getName()));
566: } finally {
567: try {
568: rs.close();
569: } catch (Exception ignore) {
570: }
571: }
572: }
573:
574: /**
575: * Gets the foreign keys of the database. They will be automatically added to
576: * the list of depending tables of the referenced table and the referencing
577: * table.<br>
578: * The caller must ensure that the parameters are not <code>null</code>.
579: *
580: * @param metaData the database meta data
581: * @param databaseSchema the database schema
582: */
583: private void getForeignKeys(DatabaseMetaData metaData,
584: DatabaseSchema databaseSchema) {
585: ResultSet rs = null;
586: try {
587: // Get foreign keys meta data
588: // getExportedKeys() gets a description of foreign keys matching the
589: // catalog, schema, and table name. Sending in null for catalog, schema
590: // and table drop them from the selection criteria so it returns all the
591: // foreign keys of the database.
592:
593: rs = metaData.getExportedKeys(null, null, null);
594:
595: if (rs == null) {
596: logger.warn(Translate
597: .get("backend.meta.get.foreign.keys.failed"));
598: return;
599: }
600:
601: DatabaseTable referencedTable = null;
602: DatabaseTable referencingTable = null;
603: while (rs.next()) {
604: // 3. PKTABLE_NAME
605: // 7. FKTABLE_NAME (8. FK_COLUMN_NAME)
606: referencedTable = databaseSchema.getTable(rs
607: .getString(3));
608: referencingTable = databaseSchema.getTable(rs
609: .getString(7));
610: if (referencedTable != null && referencingTable != null) {
611: referencedTable.addDependingTable(referencingTable
612: .getName());
613: referencingTable.addDependingTable(referencedTable
614: .getName());
615:
616: if (logger.isDebugEnabled())
617: logger.debug(Translate.get(
618: "backend.meta.found.foreign.key",
619: referencingTable.getName(),
620: referencedTable.getName()));
621: }
622: }
623: } catch (SQLException e) {
624: logger.warn(Translate
625: .get("backend.meta.get.foreign.keys.failed"), e);
626: } finally {
627: try {
628: rs.close();
629: } catch (Exception ignore) {
630: }
631: }
632: }
633:
634: /**
635: * Gets the exported keys of a given database table. The exported keys will be
636: * automatically added to the list of depending tables of the given table.<br>
637: * The caller must ensure that the parameters are not <code>null</code>.
638: *
639: * @param metaData the database meta data
640: * @param table the database table
641: */
642: private void getExportedKeys(DatabaseMetaData metaData,
643: DatabaseTable table) {
644: ResultSet rs = null;
645: try {
646: // Get exported keys meta data
647: // getExportedKeys() gets a description of exported keys matching the
648: // catalog, schema, and table name. Sending in null for catalog and
649: // schema drop them from the selection criteria.
650:
651: rs = metaData.getExportedKeys(null, table.getSchema(),
652: table.getName());
653:
654: if (rs == null) {
655: logger.warn(Translate.get(
656: "backend.meta.get.exported.keys.failed", table
657: .getSchema()
658: + "." + table.getName()));
659: return;
660: }
661:
662: String referencingTableName = null;
663: while (rs.next()) {
664: // 7. FKTABLE_NAME (8. FK_COLUMN_NAME)
665: referencingTableName = rs.getString(7);
666: if (referencingTableName == null)
667: continue;
668: if (logger.isDebugEnabled())
669: logger.debug(Translate.get(
670: "backend.meta.found.exported.key",
671: referencingTableName));
672:
673: // Set the column to unique
674: table.addDependingTable(referencingTableName);
675: }
676: } catch (SQLException e) {
677: logger.warn(Translate.get(
678: "backend.meta.get.exported.keys.failed", table
679: .getSchema()
680: + "." + table.getName()), e);
681: } finally {
682: try {
683: rs.close();
684: } catch (Exception ignore) {
685: }
686: }
687: }
688:
689: /**
690: * Gets the imported keys of a given database table. The imported keys will be
691: * automatically added to the list of depending tables of the given table.<br>
692: * The caller must ensure that the parameters are not <code>null</code>.
693: *
694: * @param metaData the database meta data
695: * @param table the database table
696: */
697: private void getImportedKeys(DatabaseMetaData metaData,
698: DatabaseTable table) {
699: ResultSet rs = null;
700: try {
701: // Get imported keys meta data
702: // getImportedKeys() gets a description of imported keys matching the
703: // catalog, schema, and table name. Sending in null for catalog and
704: // schema drop them from the selection criteria.
705:
706: rs = metaData.getImportedKeys(null, table.getSchema(),
707: table.getName());
708:
709: if (rs == null) {
710: logger.warn(Translate.get(
711: "backend.meta.get.imported.keys.failed", table
712: .getSchema()
713: + "." + table.getName()));
714: return;
715: }
716:
717: String referencedTableName = null;
718: while (rs.next()) {
719: // 3. PKTABLE_NAME (4. PKCOLUMN_NAME)
720: referencedTableName = rs.getString(3);
721: if (referencedTableName == null)
722: continue;
723: if (logger.isDebugEnabled())
724: logger.debug(Translate.get(
725: "backend.meta.found.imported.key",
726: referencedTableName));
727:
728: // Set the column to unique
729: table.addDependingTable(referencedTableName);
730: }
731: } catch (SQLException e) {
732: logger.warn(Translate.get(
733: "backend.meta.get.imported.keys.failed", table
734: .getSchema()
735: + "." + table.getName()), e);
736: } finally {
737: try {
738: rs.close();
739: } catch (Exception ignore) {
740: }
741: }
742: }
743:
744: /**
745: * Gets the primary keys of a given database table. The caller must ensure
746: * that the parameters are not <code>null</code>.
747: *
748: * @param metaData the database meta data
749: * @param table the database table
750: * @exception SQLException if an error occurs
751: */
752: private void getPrimaryKeys(DatabaseMetaData metaData,
753: DatabaseTable table) throws SQLException {
754: ResultSet rs = null;
755: try {
756: // Get primary keys meta data
757: // getPrimaryKeys() gets a description of primary keys matching the
758: // catalog, schema, and table name. Sending in null for catalog and
759: // schema drop them from the selection criteria.
760:
761: rs = metaData.getPrimaryKeys(null, table.getSchema(), table
762: .getName());
763:
764: if (rs == null) {
765: logger.warn(Translate.get(
766: "backend.meta.get.primary.keys.failed", table
767: .getSchema()
768: + "." + table.getName()));
769: return;
770: }
771:
772: String columnName = null;
773: while (rs.next()) {
774:
775: // 1 is table catalog, 2 is table schema, 3 is table name, 4 is column
776: // name
777: columnName = rs.getString(4);
778: if (columnName == null)
779: continue;
780: if (logger.isDebugEnabled())
781: logger.debug(Translate.get(
782: "backend.meta.found.primary.key",
783: columnName));
784:
785: // Set the column to unique
786: table.getColumn(columnName).setIsUnique(true);
787: }
788: } catch (SQLException e) {
789: throw new SQLException(Translate.get(
790: "backend.meta.get.primary.keys.failed", table
791: .getSchema()
792: + "." + table.getName()));
793: } finally {
794: try {
795: rs.close();
796: } catch (Exception ignore) {
797: }
798: }
799: }
800: }
|