001: package net.sourceforge.squirrel_sql.plugins.oracle.explainplan;
002:
003: /*
004: * Copyright (C) 2004 Jason Height
005: * jmheight@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:
022: import java.awt.BorderLayout;
023: import java.awt.Component;
024: import java.math.BigDecimal;
025: import java.sql.PreparedStatement;
026: import java.sql.ResultSet;
027: import java.sql.SQLException;
028: import java.sql.Statement;
029: import java.util.ArrayList;
030: import java.util.Collections;
031: import java.util.Enumeration;
032: import java.util.List;
033:
034: import javax.swing.JComponent;
035: import javax.swing.JFrame;
036: import javax.swing.JOptionPane;
037: import javax.swing.JPanel;
038: import javax.swing.JTable;
039: import javax.swing.JTree;
040: import javax.swing.table.TableColumnModel;
041: import javax.swing.tree.DefaultTreeCellRenderer;
042: import javax.swing.tree.TreeNode;
043: import javax.swing.tree.TreePath;
044:
045: import net.sourceforge.squirrel_sql.client.session.ISQLEntryPanel;
046: import net.sourceforge.squirrel_sql.client.session.ISQLPanelAPI;
047: import net.sourceforge.squirrel_sql.client.session.ISession;
048: import net.sourceforge.squirrel_sql.client.session.event.ISQLResultExecuterTabListener;
049: import net.sourceforge.squirrel_sql.client.session.event.SQLResultExecuterTabEvent;
050: import net.sourceforge.squirrel_sql.client.session.mainpanel.IResultTab;
051: import net.sourceforge.squirrel_sql.client.session.mainpanel.ISQLResultExecuter;
052: import net.sourceforge.squirrel_sql.client.session.schemainfo.SchemaInfo;
053: import net.sourceforge.squirrel_sql.fw.id.IntegerIdentifierFactory;
054: import net.sourceforge.squirrel_sql.fw.sql.ISQLConnection;
055: import net.sourceforge.squirrel_sql.fw.sql.SQLUtilities;
056: import net.sourceforge.squirrel_sql.fw.util.StringManager;
057: import net.sourceforge.squirrel_sql.fw.util.StringManagerFactory;
058: import net.sourceforge.squirrel_sql.fw.util.log.ILogger;
059: import net.sourceforge.squirrel_sql.fw.util.log.LoggerController;
060:
061: import com.sun.treetable.AbstractTreeTableModel;
062: import com.sun.treetable.JTreeTable;
063: import com.sun.treetable.TreeTableModel;
064:
065: /**
066: * This is the panel where Oracle Explain Plans are executed and results presented.
067: *
068: */
069: public class ExplainPlanExecuter extends JPanel implements
070: ISQLResultExecuter {
071:
072: private static final long serialVersionUID = 1L;
073:
074: private static final StringManager s_stringMgr = StringManagerFactory
075: .getStringManager(ExplainPlanExecuter.class);
076:
077: /** Logger for this class. */
078: private static final ILogger s_log = LoggerController
079: .createLogger(ExplainPlanExecuter.class);
080:
081: transient private ISession _session;
082: private boolean checkedPlanTable = false;
083: /** Factory for generating unique IDs for the explain plan statement ids*/
084: private IntegerIdentifierFactory _idFactory = new IntegerIdentifierFactory();
085:
086: private String _planTableName = "PLAN_TABLE";
087:
088: private static String USER_PLAN_TABLE_SQL = "SELECT 1 from USER_TABLES WHERE UPPER(TABLE_NAME) = UPPER(?)";
089:
090: private static String ALL_PLAN_TABLE_SQL = "SELECT OWNER, TABLE_NAME "
091: + "from ALL_TABLES WHERE UPPER(TABLE_NAME) = UPPER(?)";
092:
093: /**
094: * Ctor.
095: *
096: * @param session Current session.
097: *
098: * @throws IllegalArgumentException
099: * Thrown if a <TT>null</TT> <TT>ISession</TT> passed.
100: */
101: public ExplainPlanExecuter(ISession session, ISQLPanelAPI sqlpanel) {
102: super ();
103: setSession(session);
104: createGUI();
105: sqlpanel.addExecuterTabListener(new MySqlExecuterTabListener());
106: }
107:
108: public String getTitle() {
109: // i18n[oracle.explainPlan=Explain Plan]
110: return s_stringMgr.getString("oracle.explainPlan");
111: }
112:
113: public JComponent getComponent() {
114: return this ;
115: }
116:
117: /**
118: * Set the current session.
119: *
120: * @param session Current session.
121: *
122: * @throws IllegalArgumentException
123: * Thrown if a <TT>null</TT> <TT>ISession</TT> passed.
124: */
125: public synchronized void setSession(ISession session) {
126: if (session == null) {
127: throw new IllegalArgumentException("Null ISession passed");
128: }
129: sessionClosing();
130: _session = session;
131: }
132:
133: /** Current session. */
134: public ISession getSession() {
135: return _session;
136: }
137:
138: private void expandEntireTree(final JTree tree,
139: final TreePath parentPath) {
140: TreeNode parent = (TreeNode) parentPath.getLastPathComponent();
141: int size = parent.getChildCount();
142: for (int i = 0; i < size; i++) {
143: TreeNode child = parent.getChildAt(i);
144: TreePath p = parentPath.pathByAddingChild(child);
145: tree.expandPath(p);
146: expandEntireTree(tree, p);
147: }
148: }
149:
150: protected JTreeTable createTreeTable(TreeTableModel model) {
151: JTreeTable treeTable = new JTreeTable(model);
152: treeTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
153: TableColumnModel columnModel = treeTable.getColumnModel();
154: //Operation column increased
155: columnModel.getColumn(1).setPreferredWidth(300);
156: //Options column increased
157: columnModel.getColumn(2).setPreferredWidth(100);
158: JTree treeTableTree = treeTable.getTree();
159: treeTableTree.setCellRenderer(new PlanTreeCellRenderer());
160: //Expand all of the rows in the tree if the root is non null.
161: Object root = treeTableTree.getModel().getRoot();
162: if (root != null) {
163: TreePath p = new TreePath(root);
164: expandEntireTree(treeTableTree, p);
165: }
166: return treeTable;
167: }
168:
169: public void execute(ISQLEntryPanel sqlPanel) {
170: String sqlToBeExecuted = sqlPanel.getSQLToBeExecuted();
171: if (sqlToBeExecuted != null
172: && (sqlToBeExecuted.trim().length() > 0)) {
173: sqlToBeExecuted = sqlToBeExecuted.trim();
174: if (sqlToBeExecuted.endsWith(";")) {
175: sqlToBeExecuted = sqlToBeExecuted.substring(0,
176: sqlToBeExecuted.length() - 1);
177: if (s_log.isDebugEnabled()) {
178: s_log.debug("Executing SQL: " + sqlToBeExecuted);
179: }
180: }
181:
182: String statementId = "squirrel_exp_plan"
183: + _idFactory.createIdentifier();
184: PreparedStatement deletePlan = null;
185: try {
186: //Clear any previous plan
187: deletePlan = getSession().getSQLConnection()
188: .prepareStatement(
189: "delete from " + getPlanTableName()
190: + " where statement_id = ?");
191: deletePlan.setString(1, statementId);
192: deletePlan.execute();
193: deletePlan.close();
194:
195: String explainSql = "EXPLAIN PLAN SET STATEMENT_ID = '"
196: + statementId + "' INTO " + getPlanTableName()
197: + " FOR " + sqlToBeExecuted;
198: Statement explainPlan = null;
199: PreparedStatement returnPlan = null;
200: try {
201: explainPlan = getSession().getSQLConnection()
202: .createStatement();
203: explainPlan.execute(explainSql);
204:
205: String extractPlanResults = "select "
206: + " id,"
207: + " parent_id,"
208: + " LEVEL,"
209: + " STATEMENT_ID,"
210: + " TIMESTAMP,"
211: + " REMARKS,"
212: + " OPERATION,"
213: + " OPTIONS,"
214: + " OBJECT_NODE,"
215: + " OBJECT_OWNER,"
216: + " OBJECT_NAME,"
217: + " OBJECT_INSTANCE,"
218: + " OBJECT_TYPE,"
219: + " OPTIMIZER,"
220: + " SEARCH_COLUMNS,"
221: + " POSITION,"
222: + " COST,"
223: + " CARDINALITY,"
224: + " BYTES,"
225: + " OTHER_TAG,"
226: + " PARTITION_START,"
227: + " PARTITION_STOP,"
228: + " PARTITION_ID,"
229: + " OTHER,"
230: + " DISTRIBUTION "
231: + "from "
232: + getPlanTableName()
233: + " "
234: + "connect by "
235: + "prior id = parent_id and statement_id = ? "
236: + "start with id = 0 and statement_id = ? "
237: + "order by id";
238:
239: //SQLExecuterTask task = new SQLExecuterTask(_session, sql, new DefaultSQLExecuterHandler(_session));
240: //jmh to run async_session.getApplication().getThreadPool().addTask(task);
241: //jmhtask.run();
242: //Set the ? to the statement identifier
243: returnPlan = getSession().getSQLConnection()
244: .prepareStatement(extractPlanResults);
245: returnPlan.setString(1, statementId);
246: returnPlan.setString(2, statementId);
247: if (returnPlan.execute()) {
248: ResultSet rs = returnPlan.getResultSet();
249: int previousLevel = 1;
250: ExplainPlanModel.ExplainRow lastRow = null;
251: ExplainPlanModel.ExplainRow root = new ExplainPlanModel.ExplainRow(
252: null, -1, null, null, null,
253: sqlToBeExecuted, null, null, null,
254: null, null, null, null, null, null,
255: null, null, null, null, null);
256: ExplainPlanModel model = new ExplainPlanModel(
257: root);
258: while (rs.next()) {
259: BigDecimal id = rs.getBigDecimal(1);
260: BigDecimal parent_id = rs.getBigDecimal(2);
261: int level = rs.getBigDecimal(3).intValue();
262: String stmntId = rs.getString(4);
263: java.sql.Timestamp timeStamp = rs
264: .getTimestamp(5);
265: String remarks = rs.getString(6);
266: String operation = rs.getString(7);
267: String options = rs.getString(8);
268: String object_node = rs.getString(9);
269: String object_owner = rs.getString(10);
270: String object_name = rs.getString(11);
271: String object_instance = rs.getString(12);
272: String object_type = rs.getString(13);
273: String optimizer = rs.getString(14);
274: BigDecimal searchColumns = rs
275: .getBigDecimal(15);
276: BigDecimal position = rs.getBigDecimal(16);
277: BigDecimal cost = rs.getBigDecimal(17);
278: BigDecimal cardinality = rs
279: .getBigDecimal(18);
280: BigDecimal bytes = rs.getBigDecimal(19);
281: String other_tag = rs.getString(20);
282: String distribution = rs.getString(21);
283:
284: ExplainPlanModel.ExplainRow parent = null;
285:
286: if (level == 1) {
287: parent = (ExplainPlanModel.ExplainRow) model
288: .getRoot();
289: } else if (previousLevel == level) {
290: parent = ((ExplainPlanModel.ExplainRow) lastRow
291: .getParent().getParent())
292: .findChild(parent_id.intValue());
293: } else if (level > previousLevel) {
294: parent = ((ExplainPlanModel.ExplainRow) lastRow
295: .getParent())
296: .findChild(parent_id.intValue());
297: } else if (level < previousLevel) {
298: parent = (ExplainPlanModel.ExplainRow) lastRow
299: .getParent();
300: for (int i = previousLevel - level; i >= 0; i--) {
301: parent = (ExplainPlanModel.ExplainRow) parent
302: .getParent();
303: }
304: parent = parent.findChild(parent_id
305: .intValue());
306: }
307:
308: if (parent == null)
309: throw new RuntimeException(
310: "parent is null. Coding error");
311:
312: ExplainPlanModel.ExplainRow row = new ExplainPlanModel.ExplainRow(
313: parent, id.intValue(), stmntId,
314: timeStamp, remarks, operation,
315: options, object_node, object_owner,
316: object_name, object_instance,
317: object_type, optimizer,
318: searchColumns, position, cost,
319: cardinality, bytes, other_tag,
320: distribution);
321: parent.addChild(row);
322: lastRow = row;
323: previousLevel = level;
324: }
325:
326: //Unfortunately we need to remove the exising tree table component and create a
327: //new one due to limitations with replacing models in the existing
328: //sun implementation. Why on earth they couldnt formalise the tree
329: //table example on JFC (which we use) a bit more is anyones guess.
330: this .removeAll();
331: add(new javax.swing.JScrollPane(
332: createTreeTable(model)),
333: BorderLayout.CENTER);
334: }
335:
336: } catch (SQLException ex) {
337: getSession().showErrorMessage(ex);
338: s_log.error(ex);
339: } finally {
340: SQLUtilities.closeStatement(explainPlan);
341: }
342: } catch (SQLException ex) {
343: getSession().showErrorMessage(ex);
344: } finally {
345: SQLUtilities.closeStatement(deletePlan);
346: }
347: } else {
348: _session.showErrorMessage(
349: // i18n[oracle.noSql=No SQL selected for execution.]
350: s_stringMgr.getString("oracle.noSql"));
351: }
352: }
353:
354: public String getPlanTableName() {
355: return _planTableName;
356: }
357:
358: /**
359: * Sesssion is ending.
360: * Remove all listeners that this component has setup. Close all
361: * torn off result tab windows.
362: */
363: void sessionClosing() {
364: }
365:
366: private void createGUI() {
367: setLayout(new BorderLayout());
368: add(new javax.swing.JScrollPane(
369: createTreeTable(new ExplainPlanModel(null))),
370: BorderLayout.CENTER);
371: }
372:
373: /** Called when this executer is activated*/
374: void createPlanTable() {
375: if (!checkedPlanTable) {
376: // only check once per session
377: checkedPlanTable = true;
378:
379: if (!userPlanTableExists()) {
380: boolean planTableAvailable = true;
381:
382: //if doesnt exist prompt to create it.
383:
384: //i18n[explainplanexecuter.createPlanTableMsg=The Oracle Plan
385: //Table '{0}' doesnt exist in the current schema. Do you want to
386: //create it?]
387: String msg = s_stringMgr.getString(
388: "explainplanexecuter.createPlanTableMsg",
389: getPlanTableName());
390:
391: //i18n[explainplanexecuter.createPlanTableTitle=Create Plan Table]
392: String title = s_stringMgr
393: .getString("explainplanexecuter.createPlanTableTitle");
394:
395: int result = JOptionPane.showConfirmDialog(this , msg,
396: title, JOptionPane.YES_NO_OPTION,
397: JOptionPane.QUESTION_MESSAGE);
398: if (result == JOptionPane.YES_OPTION) {
399: planTableAvailable = createLocalPlanTable();
400: } else {
401: planTableAvailable = getAlternatePlanTable(getPlanTableName());
402: }
403: if (!planTableAvailable) {
404: // Tell the user that they won't be able to show the explain plan
405:
406: JFrame f = _session.getApplication().getMainFrame();
407: //i18n[explainplanexecuter.planTableUnavailable=Explain plans
408: //will be unavailable for this session]
409: msg = s_stringMgr
410: .getString("explainplanexecuter.planTableUnavailable");
411:
412: //i18n[explainplanexecuter.planTableUnavailableTitle=
413: //PLAN_TABLE not found]
414: title = s_stringMgr
415: .getString("explainplanexecuter.planTableUnavailableTitle");
416:
417: JOptionPane.showMessageDialog(f, msg, "title",
418: JOptionPane.INFORMATION_MESSAGE);
419:
420: }
421: }
422: }
423: }
424:
425: private boolean createLocalPlanTable() {
426: boolean result = true;
427: String createPlanTableSQL = getCreatePlanTableSQL(getPlanTableName());
428: Statement stmt = null;
429: try {
430: ISession session = getSession();
431: ISQLConnection con = session.getSQLConnection();
432: stmt = con.createStatement();
433: stmt.execute(createPlanTableSQL);
434: SchemaInfo schemaInfo = session.getSchemaInfo();
435: schemaInfo.refershCacheForSimpleTableName("PLAN_TABLE");
436: } catch (SQLException ex) {
437: result = false;
438: getSession().showErrorMessage(ex);
439: s_log.error(ex);
440: } finally {
441: SQLUtilities.closeStatement(stmt);
442: }
443: return result;
444: }
445:
446: /**
447: * Look for a PLAN_TABLE in another schema that we may be able to access
448: * using the following SQL:
449: *
450: * "SELECT OWNER, TABLE_NAME " +
451: "from ALL_TABLES WHERE UPPER(TABLE_NAME) = UPPER(?)";
452: *
453: */
454: private boolean getAlternatePlanTable(String planTableName) {
455: PreparedStatement pstmt = null;
456: ResultSet rs = null;
457: ArrayList<String> planTableList = new ArrayList<String>();
458: try {
459: ISQLConnection con = _session.getSQLConnection();
460: pstmt = con.prepareStatement(ALL_PLAN_TABLE_SQL);
461: pstmt.setString(1, planTableName);
462: rs = pstmt.executeQuery();
463: while (rs.next()) {
464: String owner = rs.getString(1);
465: String tableName = rs.getString(2);
466: StringBuffer tmp = new StringBuffer();
467: tmp.append(owner);
468: tmp.append(".");
469: tmp.append(tableName);
470: planTableList.add(tmp.toString());
471: }
472: } catch (SQLException e) {
473: getSession().showErrorMessage(e);
474: s_log.error(e);
475: } finally {
476: SQLUtilities.closeResultSet(rs);
477: SQLUtilities.closeStatement(pstmt);
478: }
479: if (planTableList.size() == 0) {
480: s_log.info("No PLAN_TABLE table found in view ALL_TABLES");
481: return false;
482: }
483: String[] planTables = planTableList
484: .toArray(new String[planTableList.size()]);
485:
486: JFrame f = _session.getApplication().getMainFrame();
487:
488: //i18n[explainplanexecuter.choosePlanTableMsg=Choose a PLAN_TABLE to
489: //store the result in]
490: String message = s_stringMgr
491: .getString("explainplanexecuter.choosePlanTableMsg");
492:
493: //i18n[explainplanexecuter.choosePlanTableTitle=Available PLAN_TABLEs]
494: String chooserTitle = s_stringMgr
495: .getString("explainplanexecuter.choosePlanTableMsg");
496:
497: String option = (String) JOptionPane.showInputDialog(f,
498: message, chooserTitle, JOptionPane.INFORMATION_MESSAGE,
499: null, planTables, planTables[0]);
500: if (option != null) {
501: _planTableName = option;
502: } else {
503: return false;
504: }
505: return true;
506: }
507:
508: /**
509: * Determines if the user has a PLAN_TABLE in their schema using SQL:
510: *
511: * "SELECT 1 from USER_TABLES WHERE UPPER(TABLE_NAME) = UPPER(?)";
512: *
513: * @return true if PLAN_TABLE exists; false otherwise.
514: */
515: private boolean userPlanTableExists() {
516: boolean result = false;
517: PreparedStatement stmt = null;
518: ResultSet rs = null;
519: try {
520: ISQLConnection con = getSession().getSQLConnection();
521: stmt = con.prepareStatement(USER_PLAN_TABLE_SQL);
522: stmt.setString(1, getPlanTableName());
523: rs = stmt.executeQuery();
524: if (rs.next()) {
525: result = true;
526: }
527: } catch (SQLException e) {
528: getSession().showErrorMessage(e);
529: s_log.error(e);
530: } finally {
531: SQLUtilities.closeResultSet(rs);
532: SQLUtilities.closeStatement(stmt);
533: }
534: return result;
535: }
536:
537: private String getCreatePlanTableSQL(String tableName) {
538: StringBuffer result = new StringBuffer("CREATE TABLE ");
539: result.append(tableName);
540: result.append(" (");
541: result.append("STATEMENT_ID VARCHAR2(30),");
542: result.append("TIMESTAMP DATE,");
543: result.append("REMARKS VARCHAR2(80),");
544: result.append("OPERATION VARCHAR2(30),");
545: result.append("OPTIONS VARCHAR2(30),");
546: result.append("OBJECT_NODE VARCHAR2(128),");
547: result.append("OBJECT_OWNER VARCHAR2(30),");
548: result.append("OBJECT_NAME VARCHAR2(30),");
549: result.append("OBJECT_INSTANCE NUMBER(38),");
550: result.append("OBJECT_TYPE VARCHAR2(30),");
551: result.append("OPTIMIZER VARCHAR2(255),");
552: result.append("SEARCH_COLUMNS NUMBER,");
553: result.append("ID NUMBER(38),");
554: result.append("PARENT_ID NUMBER(38),");
555: result.append("POSITION NUMBER(38),");
556: result.append("COST NUMBER(38),");
557: result.append("CARDINALITY NUMBER(38),");
558: result.append("BYTES NUMBER(38),");
559: result.append("OTHER_TAG VARCHAR2(255),");
560: result.append("PARTITION_START VARCHAR2(255),");
561: result.append("PARTITION_STOP VARCHAR2(255),");
562: result.append("PARTITION_ID NUMBER(38),");
563: result.append("OTHER LONG,");
564: result.append("DISTRIBUTION VARCHAR2(30)");
565: result.append(")");
566: return result.toString();
567: }
568:
569: private class MySqlExecuterTabListener implements
570: ISQLResultExecuterTabListener {
571: public void executerTabAdded(SQLResultExecuterTabEvent evt) {
572: }
573:
574: public void executerTabRemoved(SQLResultExecuterTabEvent evt) {
575: }
576:
577: public void executerTabActivated(SQLResultExecuterTabEvent evt) {
578: if (evt.getExecuter() == ExplainPlanExecuter.this ) {
579: createPlanTable();
580: }
581: }
582: }
583:
584: public static class ExplainPlanModel extends AbstractTreeTableModel {
585: // Names of the columns.
586: private final String[] cNames = {
587: //i18n[explainplanexecuter.enumeration=#]
588: s_stringMgr
589: .getString("explainplanexecuter.enumeration"),
590: //i18n[explainplanexecuter.operation=Operation]
591: s_stringMgr.getString("explainplanexecuter.operation"),
592: //i18n[explainplanexecuter.options=Options]
593: s_stringMgr.getString("explainplanexecuter.options"),
594: //i18n[explainplanexecuter.objectName=Object Name]
595: s_stringMgr.getString("explainplanexecuter.objectName"),
596: //i18n[explainplanexecuter.mode=Mode]
597: s_stringMgr.getString("explainplanexecuter.mode"),
598: //i18n[explainplanexecuter.cost=Cost]
599: s_stringMgr.getString("explainplanexecuter.cost"),
600: //i18n[explainplanexecuter.bytes=Bytes]
601: s_stringMgr.getString("explainplanexecuter.bytes"),
602: //i18n[explainplanexecuter.cardinality=Cardinality]
603: s_stringMgr
604: .getString("explainplanexecuter.cardinality"), };
605:
606: // Types of the columns.
607: private final Class<?>[] cTypes = { TreeTableModel.class,
608: String.class, String.class, String.class, String.class,
609: String.class, String.class, String.class };
610:
611: @SuppressWarnings("unused")
612: public static class ExplainRow implements TreeNode {
613: private ExplainRow parent;
614: private List<ExplainRow> children;
615: private int id;
616: private String idObj;
617: private String stmntId;
618: private java.sql.Timestamp timeStamp;
619: private String remarks;
620: private String operation;
621: private String options;
622: private String object_node;
623: private String object_owner;
624: private String object_name;
625: private String object_instance;
626: private String object_type;
627: private String optimizer;
628: private BigDecimal searchColumns;
629: private BigDecimal position;
630: private BigDecimal cost;
631: private BigDecimal cardinality;
632: private BigDecimal bytes;
633: private String other_tag;
634: private String distribution;
635:
636: public ExplainRow(ExplainRow parent, int id,
637: String stmntId, java.sql.Timestamp timeStamp,
638: String remarks, String operation, String options,
639: String object_node, String object_owner,
640: String object_name, String object_instance,
641: String object_type, String optimizer,
642: BigDecimal searchColumns, BigDecimal position,
643: BigDecimal cost, BigDecimal cardinality,
644: BigDecimal bytes, String other_tag,
645: String distribution) {
646: this .parent = parent;
647: this .id = id;
648: if (id == -1)
649: this .idObj = "";
650: else
651: this .idObj = Integer.toString(id);
652: this .stmntId = stmntId;
653: this .timeStamp = timeStamp;
654: this .remarks = remarks;
655: this .operation = operation;
656: this .options = options;
657: this .object_node = object_node;
658: this .object_owner = object_owner;
659: this .object_name = object_name;
660: this .object_instance = object_instance;
661: this .object_type = object_type;
662: this .optimizer = optimizer;
663: this .searchColumns = searchColumns;
664: this .position = position;
665: this .cost = cost;
666: this .cardinality = cardinality;
667: this .bytes = bytes;
668: this .other_tag = other_tag;
669: this .distribution = distribution;
670: }
671:
672: public int getID() {
673: return id;
674: }
675:
676: //TreeNode Interface
677: public TreeNode getParent() {
678: return parent;
679: }
680:
681: public Enumeration<ExplainRow> children() {
682: if (children == null) {
683: children = new ArrayList<ExplainRow>();
684: }
685: return Collections.enumeration(children);
686: }
687:
688: public boolean getAllowsChildren() {
689: return true;
690: }
691:
692: public int getIndex(TreeNode node) {
693: if (children == null)
694: return -1;
695: return children.indexOf(node);
696: }
697:
698: public void addChild(ExplainRow row) {
699: if (children == null) {
700: children = new ArrayList<ExplainRow>();
701: }
702: children.add(row);
703: }
704:
705: public boolean isLeaf() {
706: return ((children == null) || (children.size() == 0));
707: }
708:
709: public Object getValueAt(int column) {
710: switch (column) {
711: case 0:
712: return this .idObj;
713: case 1:
714: return this .operation;
715: case 2:
716: return this .options;
717: case 3:
718: return this .object_name;
719: case 4:
720: return this .optimizer;
721: case 5:
722: return this .cost;
723: case 6:
724: return this .bytes;
725: case 7:
726: return this .cardinality;
727: default:
728: return null;
729: }
730: }
731:
732: public int getChildCount() {
733: return (children == null) ? 0 : children.size();
734: }
735:
736: public TreeNode getChildAt(int child) {
737: return children.get(child);
738: }
739:
740: public ExplainRow findChild(int id) {
741: for (int i = getChildCount() - 1; i >= 0; i--) {
742: ExplainRow child = (ExplainRow) getChildAt(i);
743: if (child.getID() == id) {
744: return child;
745: }
746: }
747: return null;
748: }
749:
750: public String toString() {
751: return idObj;
752: }
753: }
754:
755: public ExplainPlanModel(ExplainRow root) {
756: super (root);
757: }
758:
759: //
760: // The TreeModel interface
761: //
762:
763: /**
764: * Returns the number of children of <code>node</code>.
765: */
766: public int getChildCount(Object node) {
767: ExplainRow er = (ExplainRow) node;
768: return er.getChildCount();
769: }
770:
771: /**
772: * Returns the child of <code>node</code> at index <code>i</code>.
773: */
774: public Object getChild(Object node, int i) {
775: ExplainRow er = (ExplainRow) node;
776: return er.getChildAt(i);
777: }
778:
779: /**
780: * Returns true if the passed in object represents a leaf, false
781: * otherwise.
782: */
783: public boolean isLeaf(Object node) {
784: return ((ExplainRow) node).isLeaf();
785: }
786:
787: //
788: // The TreeTableNode interface.
789: //
790:
791: /**
792: * Returns the number of columns.
793: */
794: public int getColumnCount() {
795: return cNames.length;
796: }
797:
798: /**
799: * Returns the name for a particular column.
800: */
801: public String getColumnName(int column) {
802: return cNames[column];
803: }
804:
805: /**
806: * Returns the class for the particular column.
807: */
808: public Class<?> getColumnClass(int column) {
809: return cTypes[column];
810: }
811:
812: /**
813: * Returns the value of the particular column.
814: */
815: public Object getValueAt(Object node, int column) {
816: ExplainRow fn = (ExplainRow) node;
817: return fn.getValueAt(column);
818: }
819: }
820:
821: private class PlanTreeCellRenderer extends DefaultTreeCellRenderer {
822:
823: private static final long serialVersionUID = 6829431667964347305L;
824:
825: public Component getTreeCellRendererComponent(JTree tree,
826: Object value, boolean selected, boolean expanded,
827: boolean leaf, int row, boolean hasFocus) {
828: super .getTreeCellRendererComponent(tree, value, selected,
829: expanded, leaf, row, hasFocus);
830: this .setIcon(null);
831: return this ;
832: }
833:
834: }
835:
836: public static void main(String[] args) {
837: javax.swing.JFrame frame = new javax.swing.JFrame(
838: "Tree Table test");
839: ExplainPlanModel.ExplainRow root = new ExplainPlanModel.ExplainRow(
840: null, -1, "root", null, null, "JMH Root", null, null,
841: null, null, null, null, null, null, null, null, null,
842: null, null, null);
843: ExplainPlanModel.ExplainRow child = new ExplainPlanModel.ExplainRow(
844: root, 0, "child", null, null, "Child 0", null, null,
845: null, null, null, null, null, null, null, null, null,
846: null, null, null);
847: ExplainPlanModel.ExplainRow child2 = new ExplainPlanModel.ExplainRow(
848: root, 1, "child 2", null, null, "Child 1", null, null,
849: null, null, null, null, null, null, null, null, null,
850: null, null, null);
851: ExplainPlanModel.ExplainRow child3 = new ExplainPlanModel.ExplainRow(
852: child2, 2, "child 3", null, null, "Child 2", null,
853: null, null, null, null, null, null, null, null, null,
854: null, null, null, null);
855: ExplainPlanModel.ExplainRow child4 = new ExplainPlanModel.ExplainRow(
856: child3, 4, "child 4", null, null, "Child 4", null,
857: null, null, null, null, null, null, null, null, null,
858: null, null, null, null);
859: ExplainPlanModel.ExplainRow child5 = new ExplainPlanModel.ExplainRow(
860: child2, 5, "child 5", null, null, "Child 5", null,
861: null, null, null, null, null, null, null, null, null,
862: null, null, null, null);
863:
864: root.addChild(child);
865: root.addChild(child2);
866: child2.addChild(child3);
867: child2.addChild(child5);
868: child3.addChild(child4);
869: TreeTableModel model = new ExplainPlanModel(root);
870: JTreeTable treeTable = new JTreeTable(model);
871: int rowCount = treeTable.getTree().getRowCount();
872: for (int i = 0; i < rowCount; i++) {
873: treeTable.getTree().expandRow(i);
874: }
875: frame.getContentPane().add(
876: new javax.swing.JScrollPane(treeTable));
877: frame.setSize(640, 480);
878: frame
879: .setDefaultCloseOperation(javax.swing.JFrame.DISPOSE_ON_CLOSE);
880: frame.setVisible(true);
881:
882: }
883:
884: /**
885: * @see net.sourceforge.squirrel_sql.client.session.mainpanel.ISQLResultExecuter#getSelectedResultTab()
886: */
887: public IResultTab getSelectedResultTab() {
888: throw new UnsupportedOperationException(
889: "ExplainPlanExecuter has no ResultTabs");
890: }
891:
892: }
|