001: /*
002: * Copyright (C) 2005 Rob Manning
003: * manningr@users.sourceforge.net
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: */
019: package net.sourceforge.squirrel_sql.plugins.dbcopy;
020:
021: import java.sql.PreparedStatement;
022: import java.sql.ResultSet;
023: import java.sql.SQLException;
024: import java.sql.Types;
025: import java.util.ArrayList;
026: import java.util.Arrays;
027: import java.util.Collection;
028: import java.util.Iterator;
029: import java.util.List;
030: import java.util.Set;
031:
032: import net.sourceforge.squirrel_sql.client.session.ISession;
033: import net.sourceforge.squirrel_sql.fw.dialects.DialectFactory;
034: import net.sourceforge.squirrel_sql.fw.dialects.DialectUtils;
035: import net.sourceforge.squirrel_sql.fw.dialects.UserCancelledOperationException;
036: import net.sourceforge.squirrel_sql.fw.sql.IDatabaseObjectInfo;
037: import net.sourceforge.squirrel_sql.fw.sql.ISQLConnection;
038: import net.sourceforge.squirrel_sql.fw.sql.ISQLDatabaseMetaData;
039: import net.sourceforge.squirrel_sql.fw.sql.ITableInfo;
040: import net.sourceforge.squirrel_sql.fw.sql.PrimaryKeyInfo;
041: import net.sourceforge.squirrel_sql.fw.sql.SQLDatabaseMetaData;
042: import net.sourceforge.squirrel_sql.fw.sql.SQLUtilities;
043: import net.sourceforge.squirrel_sql.fw.sql.TableColumnInfo;
044: import net.sourceforge.squirrel_sql.fw.util.StringManager;
045: import net.sourceforge.squirrel_sql.fw.util.StringManagerFactory;
046: import net.sourceforge.squirrel_sql.fw.util.log.ILogger;
047: import net.sourceforge.squirrel_sql.fw.util.log.LoggerController;
048: import net.sourceforge.squirrel_sql.plugins.dbcopy.event.AnalysisEvent;
049: import net.sourceforge.squirrel_sql.plugins.dbcopy.event.CopyEvent;
050: import net.sourceforge.squirrel_sql.plugins.dbcopy.event.CopyTableListener;
051: import net.sourceforge.squirrel_sql.plugins.dbcopy.event.ErrorEvent;
052: import net.sourceforge.squirrel_sql.plugins.dbcopy.event.RecordEvent;
053: import net.sourceforge.squirrel_sql.plugins.dbcopy.event.StatementEvent;
054: import net.sourceforge.squirrel_sql.plugins.dbcopy.event.TableEvent;
055: import net.sourceforge.squirrel_sql.plugins.dbcopy.prefs.DBCopyPreferenceBean;
056: import net.sourceforge.squirrel_sql.plugins.dbcopy.prefs.PreferencesManager;
057: import net.sourceforge.squirrel_sql.plugins.dbcopy.util.DBUtil;
058:
059: import org.hibernate.MappingException;
060:
061: /**
062: * This is the class that performs the table copy using database connections
063: * to two different database schemas.
064: */
065: public class CopyExecutor extends I18NBaseObject {
066:
067: /** the class that provides out session information */
068: SessionInfoProvider prov = null;
069:
070: /** the source session. This comes from prov */
071: ISession sourceSession = null;
072:
073: /** the destination session. This comes from prov */
074: ISession destSession = null;
075:
076: /** the thread we do the work in */
077: private Thread execThread = null;
078:
079: /** what value did autocommit have in dest connection when we received it */
080: private boolean originalAutoCommitValue = true;
081:
082: /** what value does autocommit have in dest connection now */
083: private boolean currentAutoCommitValue = true;
084:
085: /** the user's preferences */
086: private static DBCopyPreferenceBean prefs = PreferencesManager
087: .getPreferences();
088:
089: /** Logger for this class. */
090: private final static ILogger log = LoggerController
091: .createLogger(CopyExecutor.class);
092:
093: /** Internationalized strings for this class. */
094: private static final StringManager s_stringMgr = StringManagerFactory
095: .getStringManager(CopyExecutor.class);
096:
097: /** the list of ITableInfos that represent the user's last selection. */
098: private ArrayList<ITableInfo> selectedTableInfos = null;
099:
100: /** the CopyTableListeners that have registered with this class */
101: private ArrayList<CopyTableListener> listeners = new ArrayList<CopyTableListener>();
102:
103: /** whether or not the user cancelled the copy operation */
104: private volatile boolean cancelled = false;
105:
106: /** impl that gives us feedback from the user */
107: private UICallbacks pref = null;
108:
109: /** the start time in millis that the copy operation began */
110: private long start = 0;
111:
112: /** the finish time in millis that the copy operation began */
113: private long end = 0;
114:
115: /**
116: * Constructor.
117: *
118: * @param p the provider of information regarding what to copy where.
119: */
120: public CopyExecutor(SessionInfoProvider p) {
121: prov = p;
122: sourceSession = prov.getCopySourceSession();
123: destSession = prov.getCopyDestSession();
124: }
125:
126: /**
127: * Starts the thread that executes the copy operation.
128: */
129: public void execute() {
130: Runnable runnable = new Runnable() {
131: public void run() {
132: _execute();
133: }
134: };
135: execThread = new Thread(runnable);
136: execThread.setName("DBCopy Executor Thread");
137: execThread.start();
138: }
139:
140: /**
141: * Cancels the copy operation.
142: */
143: public void cancel() {
144: cancelled = true;
145: execThread.interrupt();
146: }
147:
148: /**
149: * Performs the table copy operation.
150: */
151: private void _execute() {
152: start = System.currentTimeMillis();
153: boolean encounteredException = false;
154: ISQLConnection destConn = destSession.getSQLConnection();
155: if (!analyzeTables()) {
156: return;
157: }
158: setupAutoCommit(destConn);
159: IDatabaseObjectInfo[] sourceObjs = prov
160: .getSourceSelectedDatabaseObjects();
161: int[] counts = getTableCounts();
162: sendCopyStarted(counts);
163: String destSchema = prov.getDestSelectedDatabaseObject()
164: .getSimpleName();
165: String destCatalog = prov.getDestSelectedDatabaseObject()
166: .getCatalogName();
167: for (int i = 0; i < sourceObjs.length; i++) {
168: if (false == sourceObjs[i] instanceof ITableInfo) {
169: continue;
170: }
171: ITableInfo sourceTI = (ITableInfo) sourceObjs[i];
172: sendTableCopyStarted(sourceTI, i + 1);
173: try {
174: int destTableCount = DBUtil.getTableCount(destSession,
175: destCatalog, destSchema, sourceTI
176: .getSimpleName(),
177: DialectFactory.DEST_TYPE);
178: if (destTableCount == -1) {
179: createTable(sourceTI);
180: }
181: if (destTableCount > 0) {
182: try {
183: String t = sourceTI.getSimpleName();
184: if (pref.appendRecordsToExisting(t)) {
185: /* Do nothing */
186: } else if (pref.deleteTableData(sourceTI
187: .getSimpleName())) {
188: // Yes || Yes to all
189: DBUtil.deleteDataInExistingTable(
190: destSession, destCatalog,
191: destSchema, sourceTI
192: .getSimpleName());
193: } else {
194: continue; // skip this table, try the next.
195: }
196:
197: } catch (UserCancelledOperationException e) {
198: cancelled = true;
199: break;
200: }
201: }
202:
203: copyTable(sourceTI, counts[i]);
204:
205: if (i == sourceObjs.length - 1 && !cancelled) {
206: // We just copied the last table. Now it is safe to copy the
207: // constraints.(Well, that is, if all FK dependencies are met
208: // in the group of tables being copied.
209: // TODO: new feature could be to examine table list for FK's
210: // in tables not in the list then prompt the user to add
211: // those missing tables to the list.
212: copyConstraints(sourceObjs);
213: }
214: if (!cancelled) {
215: sendTableCopyFinished(sourceTI, i + 1);
216: sleep(prefs.getTableDelayMillis());
217: }
218: } catch (SQLException e) {
219: encounteredException = true;
220: sendErrorEvent(ErrorEvent.SQL_EXCEPTION_TYPE, e);
221: break;
222: } catch (MappingException e) {
223: encounteredException = true;
224: sendErrorEvent(ErrorEvent.MAPPING_EXCEPTION_TYPE, e);
225: break;
226: } catch (UserCancelledOperationException e) {
227: cancelled = true;
228: break;
229: } catch (Exception e) {
230: encounteredException = true;
231: sendErrorEvent(ErrorEvent.GENERIC_EXCEPTION, e);
232: break;
233: }
234: }
235: restoreAutoCommit(destConn);
236: if (cancelled) {
237: sendErrorEvent(ErrorEvent.USER_CANCELLED_EXCEPTION_TYPE);
238: return;
239: }
240: if (encounteredException) {
241: return;
242: }
243: end = System.currentTimeMillis();
244:
245: ISession session = prov.getCopyDestSession();
246: session.getSchemaInfo().reload(
247: prov.getDestSelectedDatabaseObject());
248: session.getSchemaInfo().fireSchemaInfoUpdate();
249:
250: notifyCopyFinished();
251: }
252:
253: /**
254: * Registers the specified listener to receive copy events from this class.
255: *
256: * @param listener
257: */
258: public void addListener(CopyTableListener listener) {
259: if (listener == null) {
260: throw new IllegalArgumentException(
261: "listener cannot be null");
262: }
263: listeners.add(listener);
264: }
265:
266: /**
267: * Causes the current thread to sleep for the amount of time specified if
268: * sleepTime > 0. No effect for sleepTime <= 0.
269: *
270: * @param sleepTime time in milliseconds to make the current thread sleep.
271: */
272: private void sleep(long sleepTime) {
273: boolean shouldSleep = prefs.isDelayBetweenObjects();
274: if (!shouldSleep || sleepTime <= 0) {
275: return;
276: }
277: try {
278: Thread.sleep(sleepTime);
279: } catch (InterruptedException e) {
280: // Do Nothing
281: }
282: }
283:
284: /**
285: * For all selected tables, loop through their columns and see if the column
286: * name can be used as a column name in the destination database. This
287: * method will send an error event if a table has any column names that
288: * cannot be used in the destination database. This method just returns
289: * true if the user preference is not to test column names.
290: *
291: * @return true if the tables can be created in the destination database;
292: * false is returned otherwise.
293: */
294: private boolean analyzeTables() {
295: boolean result = true;
296: if (!prefs.isTestColumnNames()) {
297: return true;
298: }
299: if (DBUtil.sameDatabaseType(prov.getCopySourceSession(), prov
300: .getCopyDestSession())) {
301: // No need to check column name validity when source and dest are
302: // of the same type of database.
303: return true;
304: }
305: sendAnalysisStarted();
306: try {
307: IDatabaseObjectInfo[] dbObjs = prov
308: .getSourceSelectedDatabaseObjects();
309: for (int tableIdx = 0; tableIdx < dbObjs.length; tableIdx++) {
310: ITableInfo ti = (ITableInfo) dbObjs[tableIdx];
311: sendAnalyzingTable(ti, tableIdx);
312: DBUtil.validateColumnNames(ti, prov);
313: }
314: } catch (MappingException e) {
315: sendErrorEvent(ErrorEvent.MAPPING_EXCEPTION_TYPE, e);
316: result = false;
317: } catch (UserCancelledOperationException e) {
318: sendErrorEvent(ErrorEvent.USER_CANCELLED_EXCEPTION_TYPE, e);
319: result = false;
320: }
321: return result;
322: }
323:
324: /**
325: * Setup the auto-commit setting on the specified connection to
326: * the user's preference.
327: *
328: * @param con
329: */
330: private void setupAutoCommit(ISQLConnection con) {
331: boolean autoCommitPref = prefs.isAutoCommitEnabled();
332: try {
333: originalAutoCommitValue = con.getAutoCommit();
334: currentAutoCommitValue = originalAutoCommitValue;
335: if (autoCommitPref != originalAutoCommitValue) {
336: con.setAutoCommit(autoCommitPref);
337: currentAutoCommitValue = autoCommitPref;
338: }
339: } catch (SQLException e) {
340: // Don't fool around with manual commit later.
341: currentAutoCommitValue = true;
342: sendErrorEvent(ErrorEvent.SETUP_AUTO_COMMIT_TYPE, e);
343: }
344:
345: }
346:
347: /**
348: * Restore the auto-commit setting on the specified connection to the
349: * whatever it was previous to our manipulation
350: *
351: * @param con
352: */
353: private void restoreAutoCommit(ISQLConnection con) {
354: if (originalAutoCommitValue == currentAutoCommitValue) {
355: return;
356: }
357: try {
358: con.setAutoCommit(originalAutoCommitValue);
359: } catch (SQLException e) {
360: sendErrorEvent(ErrorEvent.RESTORE_AUTO_COMMIT_TYPE, e);
361: }
362: }
363:
364: private int[] getTableCounts() {
365: int[] result = null;
366:
367: ISession sourceSession = prov.getCopySourceSession();
368: IDatabaseObjectInfo[] dbObjs = prov
369: .getSourceSelectedDatabaseObjects();
370: if (dbObjs != null) {
371: result = new int[dbObjs.length];
372: selectedTableInfos = new ArrayList<ITableInfo>();
373: for (int i = 0; i < dbObjs.length; i++) {
374: if (false == dbObjs[i] instanceof ITableInfo) {
375: continue;
376: }
377: try {
378: ITableInfo ti = (ITableInfo) dbObjs[i];
379: selectedTableInfos.add(ti);
380: // This doesn't appear to work for PROGRESS RDBMS
381: //result[i] = DBUtil.getTableCount(con, ti.getSimpleName());
382: result[i] = DBUtil.getTableCount(sourceSession, ti
383: .getCatalogName(), ti.getSchemaName(), ti
384: .getSimpleName(),
385: DialectFactory.SOURCE_TYPE);
386: } catch (Exception e) {
387: log.error("", e);
388: result[i] = 0;
389: }
390: }
391: }
392: return result;
393: }
394:
395: private void sendAnalysisStarted() {
396: AnalysisEvent event = new AnalysisEvent(prov);
397: Iterator<CopyTableListener> i = listeners.iterator();
398: while (i.hasNext()) {
399: CopyTableListener listener = i.next();
400: listener.tableAnalysisStarted(event);
401: }
402: }
403:
404: private void sendAnalyzingTable(ITableInfo ti, int number) {
405: TableEvent event = new TableEvent(prov);
406: event
407: .setTableCount(prov.getSourceSelectedDatabaseObjects().length);
408: event.setTableNumber(number);
409: Iterator<CopyTableListener> i = listeners.iterator();
410: event.setTableName(ti.getSimpleName());
411: while (i.hasNext()) {
412: CopyTableListener listener = i.next();
413: listener.analyzingTable(event);
414: }
415: }
416:
417: private void sendCopyStarted(int[] tableCounts) {
418: CopyEvent event = new CopyEvent(prov);
419: event.setTableCounts(tableCounts);
420: Iterator<CopyTableListener> i = listeners.iterator();
421: while (i.hasNext()) {
422: CopyTableListener listener = i.next();
423: listener.copyStarted(event);
424: }
425: }
426:
427: private void sendTableCopyStarted(ITableInfo ti, int number) {
428: TableEvent event = new TableEvent(prov);
429: event.setTableNumber(number);
430: event
431: .setTableCount(prov.getSourceSelectedDatabaseObjects().length);
432: event.setTableName(ti.getSimpleName());
433: Iterator<CopyTableListener> i = listeners.iterator();
434: while (i.hasNext()) {
435: CopyTableListener listener = i.next();
436: listener.tableCopyStarted(event);
437: }
438: }
439:
440: private void sendTableCopyFinished(ITableInfo ti, int number) {
441: TableEvent event = new TableEvent(prov);
442: event.setTableNumber(number);
443: event
444: .setTableCount(prov.getSourceSelectedDatabaseObjects().length);
445: event.setTableName(ti.getSimpleName());
446: Iterator<CopyTableListener> i = listeners.iterator();
447: while (i.hasNext()) {
448: CopyTableListener listener = i.next();
449: listener.tableCopyFinished(event);
450: }
451: }
452:
453: /**
454: * Send an error event message to all CopyTableListeners
455: * @param type the type of the ErrorEvent.
456: */
457: private void sendErrorEvent(int type) {
458: sendErrorEvent(type, null);
459: }
460:
461: /**
462: * Send an error event message to all CopyTableListeners
463: * @param type the type of the ErrorEvent.
464: * @param e the exception that was encountered.
465: */
466: private void sendErrorEvent(int type, Exception e) {
467: ErrorEvent event = new ErrorEvent(prov, type);
468: event.setException(e);
469: Iterator<CopyTableListener> i = listeners.iterator();
470: while (i.hasNext()) {
471: CopyTableListener listener = i.next();
472: listener.handleError(event);
473: }
474: }
475:
476: private void sendRecordEvent(int number, int count) {
477: RecordEvent event = new RecordEvent(prov, number, count);
478: Iterator<CopyTableListener> i = listeners.iterator();
479: while (i.hasNext()) {
480: CopyTableListener listener = i.next();
481: listener.recordCopied(event);
482: }
483: }
484:
485: private void sendStatementEvent(String sql, String[] vals) {
486: StatementEvent event = new StatementEvent(sql,
487: StatementEvent.INSERT_RECORD_TYPE);
488: event.setBindValues(vals);
489: Iterator<CopyTableListener> i = listeners.iterator();
490: while (i.hasNext()) {
491: CopyTableListener listener = i.next();
492: listener.statementExecuted(event);
493: }
494: }
495:
496: private void notifyCopyFinished() {
497: int seconds = (int) getElapsedSeconds();
498: Iterator<CopyTableListener> i = listeners.iterator();
499: while (i.hasNext()) {
500: CopyTableListener listener = i.next();
501: listener.copyFinished(seconds);
502: }
503: }
504:
505: /**
506: *
507: * @return
508: */
509: private long getElapsedSeconds() {
510: long result = 1;
511: double elapsed = end - start;
512: if (elapsed > 1000) {
513: result = Math.round(elapsed / 1000);
514: }
515: return result;
516: }
517:
518: /**
519: *
520: * @param sourceTableInfo
521: * @param sourceTableCount
522: * @throws MappingException
523: * @throws SQLException
524: */
525: private void copyTable(ITableInfo sourceTableInfo,
526: int sourceTableCount) throws MappingException,
527: SQLException, UserCancelledOperationException {
528: PreparedStatement insertStmt = null;
529: ResultSet rs = null;
530: if (cancelled) {
531: return;
532: }
533: if (!PreferencesManager.getPreferences().isCopyData()) {
534: return;
535: }
536: ISQLConnection sourceConn = prov.getCopySourceSession()
537: .getSQLConnection();
538: ISQLConnection destConn = prov.getCopyDestSession()
539: .getSQLConnection();
540: SQLDatabaseMetaData sourceMetaData = sourceConn
541: .getSQLMetaData();
542: SQLDatabaseMetaData destMetaData = destConn.getSQLMetaData();
543: try {
544: String destSchema = prov.getDestSelectedDatabaseObject()
545: .getSimpleName();
546: ITableInfo destTableInfo = DBUtil.getTableInfo(prov
547: .getCopyDestSession(), destSchema, sourceTableInfo
548: .getSimpleName());
549:
550: TableColumnInfo[] sourceInfos = sourceMetaData
551: .getColumnInfo(sourceTableInfo);
552: TableColumnInfo[] destInfos = destMetaData
553: .getColumnInfo(destTableInfo);
554:
555: destInfos = sort(sourceInfos, destInfos, sourceTableInfo
556: .getQualifiedName(), destTableInfo
557: .getQualifiedName());
558:
559: String sourceColList = DBUtil.getColumnList(sourceInfos);
560: String destColList = DBUtil.getColumnList(destInfos);
561:
562: String selectSQL = DBUtil.getSelectQuery(prov,
563: sourceColList, sourceTableInfo);
564: String insertSQL = DBUtil.getInsertSQL(prov, destColList,
565: sourceTableInfo, destInfos.length);
566: insertStmt = destConn.prepareStatement(insertSQL);
567:
568: int count = 1;
569: int commitCount = prefs.getCommitCount();
570: int columnCount = destInfos.length;
571: String[] bindVarVals = new String[columnCount];
572:
573: boolean foundLOBType = false;
574: // Loop through source records...
575: DBUtil.setLastStatement(selectSQL);
576: rs = DBUtil.executeQuery(prov.getCopySourceSession(),
577: selectSQL);
578: DBUtil.setLastStatement(insertSQL);
579: boolean isMysql = DialectFactory.isMySQL(destSession
580: .getMetaData());
581: boolean isSourceOracle = DialectFactory
582: .isOracle(sourceSession.getMetaData());
583: boolean isDestOracle = DialectFactory.isOracle(destSession
584: .getMetaData());
585: while (rs.next() && !cancelled) {
586: // MySQL driver gets unhappy when we use the same
587: // PreparedStatement to bind null and non-null LOB variables
588: // without clearing the parameters first.
589: if (isMysql && foundLOBType) {
590: insertStmt.clearParameters();
591: }
592: StringBuilder lastStmtValuesBuffer = new StringBuilder();
593: lastStmtValuesBuffer
594: .append("\n(Bind variable values: ");
595: for (int i = 0; i < columnCount; i++) {
596:
597: int sourceColType = sourceInfos[i].getDataType();
598: // If source column is type 1111 (OTHER), try to use the
599: // column type name to find a type that isn't 1111.
600: sourceColType = DBUtil
601: .replaceOtherDataType(sourceInfos[i]);
602: sourceColType = getDateReplacement(sourceColType,
603: isSourceOracle);
604:
605: int destColType = destInfos[i].getDataType();
606: // If source column is type 1111 (OTHER), try to use the
607: // column type name to find a type that isn't 1111.
608: destColType = DBUtil
609: .replaceOtherDataType(destInfos[i]);
610: destColType = getDateReplacement(destColType,
611: isDestOracle);
612:
613: String bindVal = DBUtil.bindVariable(insertStmt,
614: sourceColType, destColType, i + 1, rs);
615: bindVarVals[i] = bindVal;
616: lastStmtValuesBuffer.append(bindVal);
617: if (i + 1 < columnCount) {
618: lastStmtValuesBuffer.append(", ");
619: }
620: if (isLOBType(destColType)) {
621: foundLOBType = true;
622: }
623: }
624: lastStmtValuesBuffer.append(")");
625: DBUtil.setLastStatementValues(lastStmtValuesBuffer
626: .toString());
627: sendStatementEvent(insertSQL, bindVarVals);
628: insertStmt.executeUpdate();
629: sendRecordEvent(count, sourceTableCount);
630: count++;
631: if (!currentAutoCommitValue) {
632: if ((count % commitCount) == 0) {
633: commitConnection(destConn);
634: }
635: }
636: sleep(prefs.getRecordDelayMillis());
637: }
638: } finally {
639: SQLUtilities.closeResultSet(rs);
640: SQLUtilities.closeStatement(insertStmt);
641: if (!currentAutoCommitValue) {
642: commitConnection(destConn);
643: }
644: }
645: }
646:
647: /**
648: * This will return a TIMESTAMP type when the specified type is a DATE and
649: * isOracle is true. This is done so that Oracle dates that have a time
650: * component, will have the time component copied correctly.
651: *
652: * @param session
653: * @param type
654: * @param isOracle
655: * @return
656: */
657: private int getDateReplacement(int type, boolean isOracle) {
658: int result = type;
659: if (isOracle && type == java.sql.Types.DATE) {
660: result = java.sql.Types.TIMESTAMP;
661: }
662: return result;
663: }
664:
665: /**
666: * Returns a boolean value indicating whether or not the specific column
667: * type is a binary or LOB column.
668: * @param columnType the JDBC type.
669: *
670: * @return true if the specified type is LOB; false otherwise.
671: */
672: private boolean isLOBType(int columnType) {
673: if (columnType == Types.BLOB || columnType == Types.CLOB
674: || columnType == Types.LONGVARBINARY
675: || columnType == Types.BINARY) {
676: return true;
677: }
678: return false;
679: }
680:
681: /**
682: * Sorts the specified destInfos array based on the order of the sourceInfos
683: * array. Not a very efficient algorthim, but it gets the job done.
684: * TODO: rewrite this using Collections sorting capability.
685: *
686: * @param sourceInfos
687: * @param destInfos
688: * @param sourceTableName
689: * @param destTableName
690: * @return a re-ordered version of the specified destInfos array
691: * @throws MappingException if the arrays differ in length or column names.
692: */
693: private TableColumnInfo[] sort(TableColumnInfo[] sourceInfos,
694: TableColumnInfo[] destInfos, String sourceTableName,
695: String destTableName) throws MappingException {
696: if (sourceInfos.length != destInfos.length) {
697: //i18n[CopyExecutor.tablecolmismatch=Column count for table {0} in
698: //source database is {1}, but column count for table {2} in
699: //destination database is {3}
700: String msg = s_stringMgr.getString(
701: "CopyExecutor.tablecolmismatch", new Object[] {
702: sourceTableName,
703: Integer.valueOf(sourceInfos.length),
704: destTableName,
705: Integer.valueOf(destInfos.length) });
706: throw new MappingException(msg);
707: }
708: ArrayList<TableColumnInfo> result = new ArrayList<TableColumnInfo>();
709:
710: for (int sourceIdx = 0; sourceIdx < sourceInfos.length; sourceIdx++) {
711: TableColumnInfo sourceInfo = sourceInfos[sourceIdx];
712: // trim the column name in case of HADB
713: String sourceColumnName = sourceInfo.getColumnName().trim();
714: boolean found = false;
715: int destIdx = 0;
716: while (!found && destIdx < destInfos.length) {
717: TableColumnInfo destInfo = destInfos[destIdx];
718: // trim the column name in case of HADB
719: String destColumnName = destInfo.getColumnName().trim();
720: if (destColumnName.equalsIgnoreCase(sourceColumnName)) {
721: result.add(destInfo);
722: found = true;
723: }
724: destIdx++;
725: }
726: if (!found) {
727: throw new MappingException("Destination table "
728: + destTableName
729: + " doesn't appear to have a column named "
730: + sourceInfo.getColumnName());
731: }
732: }
733: return result.toArray(new TableColumnInfo[destInfos.length]);
734: }
735:
736: /**
737: * Commit the specified Connection and log any SQLExceptions that might
738: * occur.
739: *
740: * @param connection
741: */
742: private void commitConnection(ISQLConnection connection) {
743: try {
744: connection.commit();
745: } catch (SQLException e) {
746: log.error("Failed to commit connection - " + connection, e);
747: }
748: }
749:
750: /**
751: * Copies the foreign key constraints. Primary keys are created in the table
752: * create statement, since some databases don't support adding primary keys
753: * after table creation. This will have no effect when using Axion as the
754: * source database.
755: *
756: * @param sourceConn
757: * @param destConn
758: * @param ti
759: * @throws SQLException
760: */
761: private void copyConstraints(IDatabaseObjectInfo[] dbObjs)
762: throws SQLException, UserCancelledOperationException {
763: if (!prefs.isCopyForeignKeys()
764: || DialectFactory.isAxion(prov.getCopySourceSession()
765: .getMetaData())) {
766: return;
767: }
768: ISQLConnection destConn = prov.getCopyDestSession()
769: .getSQLConnection();
770: for (int i = 0; i < dbObjs.length; i++) {
771: ITableInfo ti = (ITableInfo) dbObjs[i];
772: Set<String> fkStmts = DBUtil.getForeignKeySQL(prov, ti,
773: selectedTableInfos);
774: Iterator<String> it = fkStmts.iterator();
775: while (it.hasNext()) {
776: String fkSQL = it.next();
777: DBUtil.setLastStatementValues("");
778: try {
779: DBUtil.executeUpdate(destConn, fkSQL, true);
780: } catch (SQLException e) {
781: log
782: .error(
783: "Unexpected exception while attempting to "
784: + "create FK constraint using sql = "
785: + fkSQL, e);
786: }
787: }
788: }
789: }
790:
791: private void createTable(ITableInfo ti) throws SQLException,
792: UserCancelledOperationException, MappingException {
793: if (cancelled) {
794: return;
795: }
796: ISQLConnection destCon = prov.getCopyDestSession()
797: .getSQLConnection();
798: String createTableSql = DBUtil.getCreateTableSql(prov, ti);
799: DBUtil.executeUpdate(destCon, createTableSql, true);
800:
801: if (prefs.isCommitAfterTableDefs() && !currentAutoCommitValue) {
802: commitConnection(destCon);
803: }
804:
805: if (prefs.isCopyIndexDefs()) {
806: Collection<String> indices = null;
807: ISQLDatabaseMetaData sqlmd = sourceSession.getMetaData();
808: if (prefs.isCopyPrimaryKeys()) {
809: PrimaryKeyInfo[] pkList = sqlmd.getPrimaryKey(ti);
810: List<PrimaryKeyInfo> pkList2 = Arrays.asList(pkList);
811: indices = DialectUtils
812: .createIndexes(ti, sqlmd, pkList2);
813: } else {
814: indices = DialectUtils.createIndexes(ti, sqlmd, null);
815: }
816: Iterator<String> i = indices.iterator();
817: while (i.hasNext()) {
818: String createIndicesSql = i.next();
819: DBUtil.executeUpdate(destCon, createIndicesSql, true);
820: }
821: }
822: }
823:
824: /**
825: * @param pref The pref to set.
826: */
827: public void setPref(UICallbacks pref) {
828: this .pref = pref;
829: }
830:
831: /**
832: * @return Returns the pref.
833: */
834: public UICallbacks getPref() {
835: return pref;
836: }
837:
838: }
|