001: /*
002: * WbSchemaDiff.java
003: *
004: * This file is part of SQL Workbench/J, http://www.sql-workbench.net
005: *
006: * Copyright 2002-2008, Thomas Kellerer
007: * No part of this code maybe reused without the permission of the author
008: *
009: * To contact the author please send an email to: support@sql-workbench.net
010: *
011: */
012: package workbench.sql.wbcommands;
013:
014: import java.io.BufferedWriter;
015: import java.io.FileOutputStream;
016: import java.io.OutputStreamWriter;
017: import java.io.Writer;
018: import java.sql.SQLException;
019: import java.util.ArrayList;
020: import java.util.List;
021: import workbench.db.ConnectionMgr;
022: import workbench.db.ConnectionProfile;
023: import workbench.db.TableIdentifier;
024: import workbench.db.WbConnection;
025: import workbench.db.diff.SchemaDiff;
026: import workbench.gui.profiles.ProfileKey;
027: import workbench.resource.ResourceMgr;
028: import workbench.sql.SqlCommand;
029: import workbench.sql.StatementRunnerResult;
030: import workbench.storage.RowActionMonitor;
031: import workbench.util.ArgumentParser;
032: import workbench.util.ArgumentType;
033: import workbench.util.StrWriter;
034: import workbench.util.StringUtil;
035: import workbench.util.WbFile;
036:
037: /**
038: * @author support@sql-workbench.net
039: */
040: public class WbSchemaDiff extends SqlCommand {
041: public static final String VERB = "WBSCHEMADIFF";
042: public static final String PARAM_SOURCEPROFILE = "referenceProfile";
043: public static final String PARAM_SOURCEPROFILE_GROUP = "referenceGroup";
044: public static final String PARAM_TARGETPROFILE = "targetProfile";
045: public static final String PARAM_TARGETPROFILE_GROUP = "targetGroup";
046:
047: public static final String PARAM_FILENAME = "file";
048: public static final String PARAM_ENCODING = "encoding";
049: public static final String PARAM_NAMESPACE = "namespace";
050:
051: public static final String PARAM_SOURCETABLES = "referenceTables";
052: public static final String PARAM_TARGETTABLES = "targetTables";
053:
054: public static final String PARAM_SOURCESCHEMA = "referenceSchema";
055: public static final String PARAM_TARGETSCHEMA = "targetSchema";
056:
057: public static final String PARAM_EXCLUDE_TABLES = "excludeTables";
058:
059: public static final String PARAM_INCLUDE_INDEX = "includeIndex";
060: public static final String PARAM_INCLUDE_FK = "includeForeignKeys";
061: public static final String PARAM_INCLUDE_PK = "includePrimaryKeys";
062: public static final String PARAM_INCLUDE_CONSTRAINTS = "includeConstraints";
063: public static final String PARAM_INCLUDE_VIEWS = "includeViews";
064: public static final String PARAM_DIFF_JDBC_TYPES = "useJdbcTypes";
065:
066: private SchemaDiff diff;
067:
068: public WbSchemaDiff() {
069: cmdLine = new ArgumentParser();
070: cmdLine.addArgument(PARAM_SOURCEPROFILE,
071: ArgumentType.ProfileArgument);
072: cmdLine.addArgument(PARAM_SOURCEPROFILE_GROUP);
073: cmdLine.addArgument(PARAM_TARGETPROFILE,
074: ArgumentType.ProfileArgument);
075: cmdLine.addArgument(PARAM_TARGETPROFILE_GROUP);
076: cmdLine.addArgument(PARAM_FILENAME);
077: cmdLine.addArgument(PARAM_ENCODING);
078: cmdLine.addArgument(PARAM_SOURCETABLES,
079: ArgumentType.TableArgument);
080: cmdLine.addArgument(PARAM_TARGETTABLES,
081: ArgumentType.TableArgument);
082: cmdLine.addArgument(PARAM_SOURCESCHEMA);
083: cmdLine.addArgument(PARAM_TARGETSCHEMA);
084: cmdLine.addArgument(PARAM_NAMESPACE);
085: cmdLine
086: .addArgument(PARAM_INCLUDE_FK,
087: ArgumentType.BoolArgument);
088: cmdLine.addArgument(WbSchemaReport.PARAM_INCLUDE_SEQUENCES,
089: ArgumentType.BoolArgument);
090: cmdLine
091: .addArgument(PARAM_INCLUDE_PK,
092: ArgumentType.BoolArgument);
093: cmdLine.addArgument(PARAM_INCLUDE_INDEX,
094: ArgumentType.BoolArgument);
095: cmdLine.addArgument(PARAM_EXCLUDE_TABLES,
096: ArgumentType.BoolArgument);
097: cmdLine.addArgument(PARAM_INCLUDE_CONSTRAINTS,
098: ArgumentType.BoolArgument);
099: cmdLine.addArgument(PARAM_INCLUDE_VIEWS,
100: ArgumentType.BoolArgument);
101: cmdLine.addArgument(WbSchemaReport.PARAM_INCLUDE_PROCS,
102: ArgumentType.BoolArgument);
103: cmdLine.addArgument(WbSchemaReport.PARAM_INCLUDE_GRANTS,
104: ArgumentType.BoolArgument);
105: cmdLine.addArgument(PARAM_DIFF_JDBC_TYPES,
106: ArgumentType.BoolArgument);
107: }
108:
109: public String getVerb() {
110: return VERB;
111: }
112:
113: protected boolean isConnectionRequired() {
114: return false;
115: }
116:
117: public StatementRunnerResult execute(final String sql)
118: throws SQLException {
119: StatementRunnerResult result = new StatementRunnerResult();
120:
121: cmdLine.parse(getCommandLine(sql));
122:
123: if (cmdLine.getArgumentCount() == 0) {
124: result.addMessage(ResourceMgr
125: .getString("ErrDiffWrongParameters"));
126: result.setFailure();
127: return result;
128: }
129:
130: if (cmdLine.hasUnknownArguments()) {
131: setUnknownMessage(result, cmdLine, ResourceMgr
132: .getString("ErrDiffWrongParameters"));
133: return result;
134: }
135:
136: String sourceProfile = cmdLine.getValue(PARAM_SOURCEPROFILE);
137: if (sourceProfile == null)
138: sourceProfile = cmdLine.getValue("sourceprofile"); // support old name
139:
140: String sourceGroup = cmdLine
141: .getValue(PARAM_SOURCEPROFILE_GROUP);
142: ProfileKey sourceKey = null;
143: if (sourceProfile != null)
144: sourceKey = new ProfileKey(sourceProfile, sourceGroup);
145:
146: String targetProfile = cmdLine.getValue(PARAM_TARGETPROFILE);
147: String targetGroup = cmdLine
148: .getValue(PARAM_TARGETPROFILE_GROUP);
149: ProfileKey targetKey = null;
150: if (targetProfile != null)
151: targetKey = new ProfileKey(targetProfile, targetGroup);
152:
153: WbConnection targetCon = null;
154: WbConnection sourceCon = null;
155:
156: if (this .rowMonitor != null)
157: this .rowMonitor
158: .setMonitorType(RowActionMonitor.MONITOR_PLAIN);
159:
160: if (targetProfile == null
161: || (currentConnection != null && currentConnection
162: .getProfile().isProfileForKey(targetKey))) {
163: targetCon = currentConnection;
164: } else {
165: ConnectionProfile prof = ConnectionMgr.getInstance()
166: .getProfile(targetKey);
167: if (prof == null) {
168: String msg = ResourceMgr
169: .getString("ErrProfileNotFound");
170: msg = StringUtil.replace(msg, "%profile%", targetKey
171: .toString());
172: result.addMessage(msg);
173: result.setFailure();
174: return result;
175: }
176: try {
177: if (this .rowMonitor != null)
178: this .rowMonitor.setCurrentObject(ResourceMgr
179: .getString("MsgDiffConnectingTarget"), -1,
180: -1);
181: targetCon = ConnectionMgr.getInstance().getConnection(
182: targetKey, "Wb-Diff-Target");
183: } catch (Exception e) {
184: result.addMessage(ResourceMgr
185: .getString("ErrDiffCouldNotConnectTarget"));
186: result.setFailure();
187: return result;
188: }
189: }
190:
191: if (sourceProfile == null
192: || (currentConnection != null && currentConnection
193: .getProfile().isProfileForKey(sourceKey))) {
194: sourceCon = currentConnection;
195: } else {
196: ConnectionProfile prof = ConnectionMgr.getInstance()
197: .getProfile(sourceKey);
198: if (prof == null) {
199: String msg = ResourceMgr
200: .getString("ErrProfileNotFound");
201: msg = StringUtil.replace(msg, "%profile%", sourceKey
202: .toString());
203: result.addMessage(msg);
204: result.setFailure();
205: return result;
206: }
207:
208: try {
209: if (this .rowMonitor != null)
210: this .rowMonitor.setCurrentObject(ResourceMgr
211: .getString("MsgDiffConnectingSource"), -1,
212: -1);
213: sourceCon = ConnectionMgr.getInstance().getConnection(
214: sourceKey, "Wb-Diff-Source");
215: } catch (Exception e) {
216: result.addMessage(ResourceMgr
217: .getString("ErrDiffCouldNotConnectSource"));
218: result.setFailure();
219: // disconnect the target connection only if it was created by this command
220: if (targetCon.getId().startsWith("Wb-Diff")) {
221: try {
222: targetCon.disconnect();
223: } catch (Exception th) {
224: }
225: }
226: return result;
227: }
228: }
229:
230: this .diff = new SchemaDiff(sourceCon, targetCon);
231: diff.setMonitor(this .rowMonitor);
232:
233: // this needs to be set before the tables are defined!
234: diff.setIncludeForeignKeys(cmdLine.getBoolean(PARAM_INCLUDE_FK,
235: true));
236: diff.setIncludeIndex(cmdLine.getBoolean(PARAM_INCLUDE_INDEX,
237: true));
238: diff.setIncludePrimaryKeys(cmdLine.getBoolean(PARAM_INCLUDE_PK,
239: true));
240: diff.setIncludeTableConstraints(cmdLine.getBoolean(
241: PARAM_INCLUDE_CONSTRAINTS, true));
242: diff.setIncludeViews(cmdLine.getBoolean(PARAM_INCLUDE_VIEWS,
243: true));
244: diff.setCompareJdbcTypes(cmdLine.getBoolean(
245: PARAM_DIFF_JDBC_TYPES, false));
246: diff.setIncludeProcedures(cmdLine.getBoolean(
247: WbSchemaReport.PARAM_INCLUDE_PROCS, false));
248: diff.setIncludeTableGrants(cmdLine.getBoolean(
249: WbSchemaReport.PARAM_INCLUDE_GRANTS, false));
250: diff.setIncludeSequences(cmdLine.getBoolean(
251: WbSchemaReport.PARAM_INCLUDE_SEQUENCES, false));
252: //diff.setIncludeComments(cmdLine.getBoolean(PARAM_INCLUDE_COMMENTS, false));
253:
254: String refTables = cmdLine.getValue(PARAM_SOURCETABLES);
255: String tarTables = cmdLine.getValue(PARAM_TARGETTABLES);
256:
257: if (refTables == null) {
258: String refSchema = cmdLine.getValue(PARAM_SOURCESCHEMA);
259: String targetSchema = cmdLine.getValue(PARAM_TARGETSCHEMA);
260: String excludeTables = cmdLine
261: .getValue(PARAM_EXCLUDE_TABLES);
262:
263: if (refSchema == null && targetSchema == null) {
264: if (sourceCon == targetCon) {
265: result
266: .addMessage(ResourceMgr
267: .getString("ErrDiffSameConnectionNoTableSelection"));
268: result.setFailure();
269: if (targetCon.getId().startsWith("Wb-Diff")) {
270: try {
271: targetCon.disconnect();
272: } catch (Exception th) {
273: }
274: }
275: if (sourceCon.getId().startsWith("Wb-Diff")) {
276: try {
277: sourceCon.disconnect();
278: } catch (Exception th) {
279: }
280: }
281: return result;
282: }
283: if (excludeTables != null) {
284: List<String> l = StringUtil.stringToList(
285: excludeTables, ",", true);
286: diff.setExcludeTables(l);
287: }
288: diff.compareAll();
289: } else {
290: if (excludeTables != null) {
291: List<String> l = StringUtil.stringToList(
292: excludeTables, ",", true);
293: diff.setExcludeTables(l);
294: }
295: diff.setSchemas(refSchema, targetSchema);
296: }
297: } else if (tarTables == null) {
298: List<String> rl = StringUtil.stringToList(refTables, ",",
299: true, true);
300: List<TableIdentifier> tables = new ArrayList<TableIdentifier>(
301: rl.size());
302: String ttype = this .currentConnection.getMetadata()
303: .getTableTypeName();
304: for (String tname : rl) {
305: TableIdentifier tbl = new TableIdentifier(tname);
306: tbl.setType(ttype);
307: tables.add(tbl);
308: }
309: diff.setTables(tables);
310: } else {
311: List<String> rl = StringUtil.stringToList(refTables, ",",
312: true, true);
313: List<String> tl = StringUtil.stringToList(tarTables, ",",
314: true, true);
315: if (rl.size() != tl.size()) {
316: result.addMessage(ResourceMgr
317: .getString("ErrDiffTableListNoMatch"));
318: result.setFailure();
319: return result;
320: }
321: diff.setTableNames(rl, tl);
322: }
323:
324: Writer out = null;
325: boolean outputToConsole = false;
326: WbFile output = evaluateFileArgument(cmdLine
327: .getValue(PARAM_FILENAME));
328:
329: try {
330: if (output == null) {
331: out = new StrWriter(5000);
332: outputToConsole = true;
333: } else {
334: String encoding = cmdLine.getValue(PARAM_ENCODING);
335: if (encoding == null) {
336: encoding = diff.getEncoding();
337: } else {
338: diff.setEncoding(encoding);
339: }
340: out = new BufferedWriter(new OutputStreamWriter(
341: new FileOutputStream(output), encoding),
342: 256 * 1024);
343: }
344:
345: // this will start the actual diff process
346: if (!diff.isCancelled()) {
347: diff.writeXml(out);
348: }
349: } catch (Exception e) {
350: e.printStackTrace();
351: } finally {
352: try {
353: out.close();
354: } catch (Exception th) {
355: }
356: }
357:
358: if (diff.isCancelled()) {
359: result
360: .addMessage(ResourceMgr
361: .getString("MsgDiffCancelled"));
362: } else {
363: if (outputToConsole) {
364: result.addMessage(out.toString());
365: } else {
366: String msg = ResourceMgr
367: .getString("MsgDiffFileWritten")
368: + " " + output.getFullPath();
369: result.addMessage(msg);
370: }
371: }
372: return result;
373: }
374:
375: public void cancel() {
376: if (this.diff != null)
377: this.diff.cancel();
378: }
379: }
|