/*
Derby - Class org.apache.derby.impl.sql.execute.xplain.XPLAINSystemTableVisitor
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package org.apache.derby.impl.sql.execute.xplain;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
import org.apache.derby.catalog.UUID;
import org.apache.derby.iapi.jdbc.ConnectionContext;
import org.apache.derby.iapi.error.StandardException;
import org.apache.derby.iapi.services.info.JVMInfo;
import org.apache.derby.iapi.sql.Activation;
import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
import org.apache.derby.iapi.sql.dictionary.DataDictionary;
import org.apache.derby.impl.sql.catalog.XPLAINResultSetDescriptor;
import org.apache.derby.impl.sql.catalog.XPLAINResultSetTimingsDescriptor;
import org.apache.derby.impl.sql.catalog.XPLAINScanPropsDescriptor;
import org.apache.derby.impl.sql.catalog.XPLAINSortPropsDescriptor;
import org.apache.derby.impl.sql.catalog.XPLAINStatementDescriptor;
import org.apache.derby.impl.sql.catalog.XPLAINStatementTimingsDescriptor;
import org.apache.derby.iapi.sql.execute.RunTimeStatistics;
import org.apache.derby.iapi.sql.execute.xplain.XPLAINVisitor;
import org.apache.derby.iapi.sql.execute.ResultSetStatistics;
/**
* This is the Visitor, which explains the information and stores the statistics in
* the system catalogs. It traverses the result set statistics tree and extracts the
* information.
*
*/
public class XPLAINSystemTableVisitor implements XPLAINVisitor {
private boolean no_call_stmts = true;
// ---------------------------------------------------------
// member variables
// ---------------------------------------------------------
// the needed system objects for writing to the dictionary
private LanguageConnectionContext lcc;
private DataDictionary dd;
// the stmt activation object
private Activation activation;
// a flag which is used to reflect if the statistics timings is on
private boolean considerTimingInformation = false;
// the different tuple descriptors describing the query characteristics
// regarding the stmt
private XPLAINStatementDescriptor stmt;
private XPLAINStatementTimingsDescriptor stmtTimings = null;
private UUID stmtUUID; // the UUID to save for the resultsets
// now the lists of descriptors regarding the resultsets
private List rsets; // for the resultset descriptors
private List rsetsTimings; // for the resultset timings descriptors
private List sortrsets; // for the sort props descriptors
private List scanrsets; // fot the scan props descriptors
// the number of children of the current explained node
private int noChildren;
// this stack keeps track of the result set UUIDs, which get popped by the
// children of the current explained node
private Stack UUIDStack;
// ---------------------------------------------------------
// Constructor
// ---------------------------------------------------------
public XPLAINSystemTableVisitor(){
// System.out.println("System Table Visitor created...");
// initialize lists
rsets = new ArrayList();
rsetsTimings = new ArrayList();
sortrsets = new ArrayList();
scanrsets = new ArrayList();
// init UUIDStack
UUIDStack = new Stack();
}
/** helper method, which pushes the UUID,
* "number of Children" times onto the UUIDStack.
* @param uuid the UUID to push
*/
private void pushUUIDnoChildren(UUID uuid){
for (int i=0;i<noChildren;i++){
UUIDStack.push(uuid);
}
}
// ---------------------------------------------------------
// XPLAINVisitor Implementation
// ---------------------------------------------------------
/**
* this method only stores the current number of children of the current explained node.
* The child nodes then can re-use this information.
*/
public void setNumberOfChildren(int noChildren) {
this.noChildren = noChildren;
}
/**
* Visit this node, calling back to it to get details.
*
* This method visits the RS Statisitcs node, calling back to the
* node to get detailed descriptor information about it.
*/
public void visit(ResultSetStatistics statistics)
{
UUID timingID = null;
if(considerTimingInformation){
timingID = dd.getUUIDFactory().createUUID();
rsetsTimings.add(
statistics.getResultSetTimingsDescriptor(timingID));
}
UUID sortID = dd.getUUIDFactory().createUUID();
Object sortRSDescriptor = statistics.getSortPropsDescriptor(sortID);
if (sortRSDescriptor != null)
sortrsets.add(sortRSDescriptor);
else
sortID = null;
UUID scanID = dd.getUUIDFactory().createUUID();
Object scanRSDescriptor = statistics.getScanPropsDescriptor(scanID);
if (scanRSDescriptor != null)
scanrsets.add(scanRSDescriptor);
else
scanID = null;
UUID rsID = dd.getUUIDFactory().createUUID();
rsets.add(statistics.getResultSetDescriptor(rsID,
UUIDStack.empty()? null: (UUID)UUIDStack.pop(),
scanID, sortID, stmtUUID, timingID));
pushUUIDnoChildren(rsID);
}
/**
* This method resets the visitor. Gets called right before explanation
* to make sure all needed objects exist and are up to date and the lists are cleared
*/
public void reset() {
lcc = activation.getLanguageConnectionContext();
dd = lcc.getDataDictionary();
}
/** the interface method, which gets called by the Top-ResultSet, which starts
* the tree traversal.
*/
public void doXPLAIN(RunTimeStatistics rss, Activation activation)
throws StandardException
{
// save this activation
this.activation = activation;
// reset this visitor
reset();
// get the timings settings
considerTimingInformation = lcc.getStatisticsTiming();
// placeholder for the stmt timings UUID
UUID stmtTimingsUUID = null;
//1. create new stmt timings descriptor
if (considerTimingInformation){
stmtTimingsUUID = dd.getUUIDFactory().createUUID();
Timestamp endExeTS = rss.getEndExecutionTimestamp();
Timestamp beginExeTS = rss.getBeginExecutionTimestamp();
long exeTime;
if (endExeTS!=null && beginExeTS!=null){
exeTime = endExeTS.getTime() - beginExeTS.getTime();
} else {
exeTime = 0;
}
stmtTimings = new XPLAINStatementTimingsDescriptor(
stmtTimingsUUID, // the Timing UUID
new Long(rss.getParseTimeInMillis()), // the Parse Time
new Long(rss.getBindTimeInMillis()), // the Bind Time
new Long(rss.getOptimizeTimeInMillis()), // the Optimize Time
new Long(rss.getGenerateTimeInMillis()), // the Generate Time
new Long(rss.getCompileTimeInMillis()), // the Compile Time
new Long(exeTime), // the Execute Time, TODO resolve why getExecutionTime() returns 0
rss.getBeginCompilationTimestamp(), // the Begin Compilation TS
rss.getEndCompilationTimestamp(), // the End Compilation TS
rss.getBeginExecutionTimestamp(), // the Begin Execution TS
rss.getEndExecutionTimestamp() // the End Execution TS
);
}
// 2. create new Statement Descriptor
// create new UUID
stmtUUID = dd.getUUIDFactory().createUUID();
// extract stmt type
String type = XPLAINUtil.getStatementType(rss.getStatementText());
// don`t explain CALL Statements, quick implementation
// TODO improve usability to switch between call stmt explanation on or off
if (type.equalsIgnoreCase("C") && no_call_stmts) return;
// get transaction ID
String xaID = lcc.getTransactionExecute().getTransactionIdString();
// get session ID
String sessionID = Integer.toString(lcc.getInstanceNumber());
// get the JVM ID
String jvmID = Integer.toString(JVMInfo.JDK_ID);
// get the OS ID
String osID = System.getProperty("os.name");
// the current system time
long current = System.currentTimeMillis();
// the xplain type
String XPLAINtype = lcc.getXplainOnlyMode() ?
XPLAINUtil.XPLAIN_ONLY : XPLAINUtil.XPLAIN_FULL;
// the xplain time
Timestamp time = new Timestamp(current);
// the thread id
String threadID = Thread.currentThread().toString();
stmt = new XPLAINStatementDescriptor(
stmtUUID, // unique statement UUID
rss.getStatementName(), // the statement name
type, // the statement type
rss.getStatementText(), // the statement text
jvmID, // the JVM ID
osID, // the OS ID
XPLAINtype, // the EXPLAIN tpye
time, // the EXPLAIN Timestamp
threadID, // the Thread ID
xaID, // the transaction ID
sessionID, // the Session ID
lcc.getDbname(), // the Database name
lcc.getDrdaID(), // the DRDA ID
stmtTimingsUUID // Timing ID, if available
);
try {
// add it to system catalog
addStmtDescriptorsToSystemCatalog();
// get TopRSS and start the traversal of the RSS-tree
rss.acceptFromTopResultSet(this);
// add the filled lists to the dictionary
addArraysToSystemCatalogs();
}
catch (SQLException e)
{
throw StandardException.plainWrapException(e);
}
// clean up to free kept resources
clean();
}
// ---------------------------------------------------------
// helper methods
// ---------------------------------------------------------
/**
* This method cleans up things after explanation. It frees kept
* resources and still holded references.
*/
private void clean(){
// forget about all the system objects
activation = null;
lcc = null;
dd = null;
// forget about the stmt descriptors and the Stmt UUID
stmtUUID = null;
stmt = null;
stmtTimings = null;
// reset the descriptor lists to keep memory low
rsets.clear();
rsetsTimings.clear();
sortrsets.clear();
scanrsets.clear();
// clear stack, although it must be already empty...
UUIDStack.clear();
}
/**
* Open a nested Connection with which to execute INSERT statements.
*/
private Connection getDefaultConn()throws SQLException
{
ConnectionContext cc = (ConnectionContext)
lcc.getContextManager().getContext(ConnectionContext.CONTEXT_ID);
return cc.getNestedConnection(true);
}
/**
* This method writes only the stmt and its timing descriptor
* to the dataDictionary
*
*/
private void addStmtDescriptorsToSystemCatalog()
throws StandardException, SQLException
{
boolean statsSave = lcc.getRunTimeStatisticsMode();
lcc.setRunTimeStatisticsMode(false);
Connection conn = getDefaultConn();
PreparedStatement ps = conn.prepareStatement(
(String)lcc.getXplainStatement("SYSXPLAIN_STATEMENTS"));
stmt.setStatementParameters(ps);
ps.executeUpdate();
ps.close();
if(considerTimingInformation)
{
ps = conn.prepareStatement(
(String)lcc.getXplainStatement("SYSXPLAIN_STATEMENT_TIMINGS"));
stmtTimings.setStatementParameters(ps);
ps.executeUpdate();
ps.close();
}
conn.close();
lcc.setRunTimeStatisticsMode(statsSave);
}
/**
* This method writes the created descriptor arrays
* to the cooresponding system catalogs.
*/
private void addArraysToSystemCatalogs()
throws StandardException, SQLException
{
Iterator iter;
boolean statsSave = lcc.getRunTimeStatisticsMode();
lcc.setRunTimeStatisticsMode(false);
Connection conn = getDefaultConn();
PreparedStatement ps = conn.prepareStatement(
(String)lcc.getXplainStatement("SYSXPLAIN_RESULTSETS"));
iter = rsets.iterator();
while (iter.hasNext())
{
XPLAINResultSetDescriptor rset =
(XPLAINResultSetDescriptor)iter.next();
rset.setStatementParameters(ps);
ps.executeUpdate();
}
ps.close();
// add the resultset timings descriptors, if timing is on
if(considerTimingInformation)
{
ps = conn.prepareStatement(
(String)lcc.getXplainStatement("SYSXPLAIN_RESULTSET_TIMINGS"));
iter = rsetsTimings.iterator();
while (iter.hasNext())
{
XPLAINResultSetTimingsDescriptor rsetT =
(XPLAINResultSetTimingsDescriptor)iter.next();
rsetT.setStatementParameters(ps);
ps.executeUpdate();
}
ps.close();
}
ps = conn.prepareStatement(
(String)lcc.getXplainStatement("SYSXPLAIN_SCAN_PROPS"));
iter = scanrsets.iterator();
while (iter.hasNext())
{
XPLAINScanPropsDescriptor scanProps =
(XPLAINScanPropsDescriptor)iter.next();
scanProps.setStatementParameters(ps);
ps.executeUpdate();
}
ps.close();
ps = conn.prepareStatement(
(String)lcc.getXplainStatement("SYSXPLAIN_SORT_PROPS"));
iter = sortrsets.iterator();
while (iter.hasNext())
{
XPLAINSortPropsDescriptor sortProps =
(XPLAINSortPropsDescriptor)iter.next();
sortProps.setStatementParameters(ps);
ps.executeUpdate();
}
ps.close();
conn.close();
lcc.setRunTimeStatisticsMode(statsSave);
}
}