001: package net.sourceforge.squirrel_sql.client.session.action;
002:
003: /*
004: * Copyright (C) 2006 Rob Manning
005: * manningr@users.sourceforge.net
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: */
021: import java.sql.PreparedStatement;
022: import java.sql.ResultSet;
023: import java.sql.SQLException;
024: import java.util.HashSet;
025: import java.util.List;
026:
027: import net.sourceforge.squirrel_sql.client.gui.ProgessCallBackDialog;
028: import net.sourceforge.squirrel_sql.client.session.IObjectTreeAPI;
029: import net.sourceforge.squirrel_sql.client.session.ISession;
030: import net.sourceforge.squirrel_sql.client.session.SQLExecuterTask;
031: import net.sourceforge.squirrel_sql.fw.dialects.DialectFactory;
032: import net.sourceforge.squirrel_sql.fw.gui.GUIUtils;
033: import net.sourceforge.squirrel_sql.fw.sql.ITableInfo;
034: import net.sourceforge.squirrel_sql.fw.sql.SQLDatabaseMetaData;
035: import net.sourceforge.squirrel_sql.fw.sql.SQLUtilities;
036: import net.sourceforge.squirrel_sql.fw.util.ICommand;
037: import net.sourceforge.squirrel_sql.fw.util.StringManager;
038: import net.sourceforge.squirrel_sql.fw.util.StringManagerFactory;
039: import net.sourceforge.squirrel_sql.fw.util.log.ILogger;
040: import net.sourceforge.squirrel_sql.fw.util.log.LoggerController;
041:
042: /**
043: * @version $Id: DeleteTablesCommand.java,v 1.6 2007/04/08 11:48:55 manningr Exp $
044: * @author Rob Manning
045: */
046: public class DeleteTablesCommand implements ICommand {
047: /** Logger for this class. */
048: private final ILogger s_log = LoggerController
049: .createLogger(DeleteTablesCommand.class);
050:
051: /** Internationalized strings for this class */
052: private static final StringManager s_stringMgr = StringManagerFactory
053: .getStringManager(DeleteTablesCommand.class);
054:
055: static interface i18n {
056:
057: //i18n[DeleteTablesCommand.progressDialogTitle=Analyzing tables to delete]
058: String PROGRESS_DIALOG_TITLE = s_stringMgr
059: .getString("DeleteTablesCommand.progressDialogTitle");
060:
061: //i18n[DeleteTablesCommand.loadingPrefix=Analyzing table:]
062: String LOADING_PREFIX = s_stringMgr
063: .getString("DeleteTablesCommand.loadingPrefix");
064:
065: }
066:
067: /** Current session. */
068: private final ISession _session;
069:
070: /** Tables that have records to be deleted. */
071: private final List<ITableInfo> _tables;
072:
073: /**
074: * A set of materialized view names in the same schema as the table(s)
075: * being deleted
076: */
077: private HashSet<String> matViewLookup = null;
078:
079: /** API for the current tree. */
080: private IObjectTreeAPI _tree;
081:
082: /**
083: * Ctor.
084: *
085: * @param session Current session..
086: * @param tables Array of <TT>IDatabaseObjectInfo</TT> objects
087: * representing the tables to be deleted.
088: *
089: * @throws IllegalArgumentException
090: * Thrown if a <TT>null</TT> <TT>ISession</TT> passed.
091: */
092: public DeleteTablesCommand(IObjectTreeAPI tree,
093: List<ITableInfo> tables) {
094: super ();
095: if (tree == null) {
096: throw new IllegalArgumentException("ISession == null");
097: }
098: if (tables == null) {
099: throw new IllegalArgumentException(
100: "IDatabaseObjectInfo[] == null");
101: }
102:
103: _session = tree.getSession();
104: _tree = tree;
105: _tables = tables;
106: }
107:
108: /**
109: * Delete records from the selected tables in the object tree.
110: */
111: public void execute() {
112: ProgessCallBackDialog cb = new ProgessCallBackDialog(_session
113: .getApplication().getMainFrame(),
114: i18n.PROGRESS_DIALOG_TITLE, _tables.size());
115:
116: cb.setLoadingPrefix(i18n.LOADING_PREFIX);
117: DeleteExecuter executer = new DeleteExecuter(cb);
118: _session.getApplication().getThreadPool().addTask(executer);
119: }
120:
121: private class DeleteExecuter implements Runnable {
122:
123: ProgessCallBackDialog _cb = null;
124:
125: public DeleteExecuter(ProgessCallBackDialog cb) {
126: _cb = cb;
127: }
128:
129: public void run() {
130: final SQLDatabaseMetaData md = _session.getSQLConnection()
131: .getSQLMetaData();
132: List<ITableInfo> orderedTables = _tables;
133: try {
134: orderedTables = SQLUtilities.getDeletionOrder(_tables,
135: md, _cb);
136: } catch (Exception e) {
137: s_log
138: .error(
139: "Unexpected exception while attempting to order tables",
140: e);
141: }
142: final String sqlSep = _session.getQueryTokenizer()
143: .getSQLStatementSeparator();
144: String cascadeClause = null;
145: try {
146: cascadeClause = md.getCascadeClause();
147: } catch (SQLException e) {
148: s_log
149: .error(
150: "Unexpected exception while attempting to get cascade clause",
151: e);
152: }
153: final StringBuilder buf = new StringBuilder();
154: for (ITableInfo ti : orderedTables) {
155: // Can't delete records in snapshots (Oracle materialized views)
156: if (isMaterializedView(ti, _session)) {
157: continue;
158: }
159: buf.append("DELETE FROM ")
160: .append(ti.getQualifiedName());
161: if (cascadeClause != null && !cascadeClause.equals("")) {
162: buf.append(" ").append(cascadeClause);
163: }
164: buf.append(sqlSep).append(" ").append('\n');
165: }
166: if (buf.length() == 0) {
167: return;
168: }
169: SQLExecuterTask executer = new SQLExecuterTask(_session,
170: buf.toString(), null);
171:
172: // Execute the sql synchronously
173: executer.run();
174:
175: GUIUtils.processOnSwingEventThread(new Runnable() {
176: public void run() {
177: _tree.refreshSelectedNodes();
178: }
179: });
180: }
181: }
182:
183: /**
184: * Returns a boolean value indicating whether or not the specified table
185: * info is not only a table, but also a materialized view.
186: *
187: * @param ti
188: * @param session
189: * @return
190: */
191: private boolean isMaterializedView(ITableInfo ti, ISession session) {
192: if (!DialectFactory.isOracle(session.getMetaData())) {
193: // Only Oracle supports materialized views directly.
194: return false;
195: }
196: if (matViewLookup == null) {
197: initMatViewLookup(session, ti.getSchemaName());
198: }
199: return matViewLookup.contains(ti.getSimpleName());
200: }
201:
202: private void initMatViewLookup(ISession session, String schema) {
203: matViewLookup = new HashSet<String>();
204: // There is no good way using JDBC metadata to tell if the table is a
205: // materialized view. So, we need to query the data dictionary to find
206: // that out. Get all table names whose comment indicates that they are
207: // a materialized view.
208: String sql = "SELECT TABLE_NAME FROM ALL_TAB_COMMENTS "
209: + "where COMMENTS like 'snapshot%' " + "and OWNER = ? ";
210:
211: PreparedStatement stmt = null;
212: ResultSet rs = null;
213: try {
214: stmt = session.getSQLConnection().prepareStatement(sql);
215: stmt.setString(1, schema);
216: rs = stmt.executeQuery();
217: if (rs.next()) {
218: String tableName = rs.getString(1);
219: matViewLookup.add(tableName);
220: }
221: } catch (SQLException e) {
222: s_log.error(
223: "Unexpected exception while attempting to find mat. views "
224: + "in schema: " + schema, e);
225: } finally {
226: if (rs != null)
227: try {
228: rs.close();
229: } catch (SQLException ex) {
230: }
231: if (stmt != null)
232: try {
233: stmt.close();
234: } catch (SQLException ex) {
235: }
236: }
237:
238: }
239:
240: }
|