blob: 2c89651e0701b041844a2dca12e13b910253368d [file] [log] [blame]
/*
Derby - Class org.apache.derbyTesting.functionTests.tests.lang.XMLOptimizerTraceTest
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.derbyTesting.functionTests.tests.lang;
import java.io.File;
import java.net.URL;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import junit.framework.Test;
import org.apache.derby.iapi.services.info.JVMInfo;
import org.apache.derbyTesting.junit.BaseTestSuite;
import org.apache.derbyTesting.junit.SupportFilesSetup;
import org.apache.derbyTesting.junit.TestConfiguration;
/**
* <p>
* Test xml-based optimizer tracing, introduced by DERBY-6211.
* </p>
*/
public class XMLOptimizerTraceTest extends GeneratedColumnsHelper
{
///////////////////////////////////////////////////////////////////////////////////
//
// CONSTANTS
//
///////////////////////////////////////////////////////////////////////////////////
private static final String TRACE_FILE_NAME = "xott.xml";
private static final String SAVED_TRACE_NAME = "xmlOptimizer.trace";
private static final String FILE_EXISTS = "XIE0S";
///////////////////////////////////////////////////////////////////////////////////
//
// STATE
//
///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////
//
// CONSTRUCTOR
//
///////////////////////////////////////////////////////////////////////////////////
/**
* Create a new instance.
*/
public XMLOptimizerTraceTest(String name)
{
super(name);
}
///////////////////////////////////////////////////////////////////////////////////
//
// JUnit BEHAVIOR
//
///////////////////////////////////////////////////////////////////////////////////
/**
* Construct top level suite in this JUnit test
*/
public static Test suite()
{
String[] testFiles = new String[] { "functionTests/tests/lang/" + SAVED_TRACE_NAME };
BaseTestSuite suite = new BaseTestSuite("XMLOptimizerTraceTest");
suite.addTest( TestConfiguration.defaultSuite( XMLOptimizerTraceTest.class ) );
return new SupportFilesSetup( TestConfiguration.singleUseDatabaseDecorator( suite ), testFiles );
}
protected void setUp()
throws Exception
{
super.setUp();
Connection conn = getConnection();
if ( !routineExists( conn, "INTEGERLIST" ) )
{
goodStatement
(
conn,
"create function integerList()\n" +
"returns table( a int, b int, c int, d int )\n" +
"language java parameter style derby_jdbc_result_set no sql\n" +
"external name 'org.apache.derbyTesting.functionTests.tests.lang.RestrictedVTITest.integerList'\n"
);
}
if ( !routineExists( conn, "GETRESULTSETMETADATA" ) )
{
goodStatement
(
conn,
"create function getResultSetMetaData( query varchar( 32672 ) )\n" +
"returns table\n" +
"(\n" +
" getCatalogName varchar( 32672 ),\n" +
" getColumnClassName varchar( 32672 ),\n" +
" getColumnDisplaySize int,\n" +
" getColumnLabel varchar( 32672 ),\n" +
" getColumnName varchar( 32672 ),\n" +
" getColumnType int,\n" +
" getColumnTypeName varchar( 32672 ),\n" +
" getPrecision int,\n" +
" getScale int,\n" +
" getSchemaName varchar( 32672 ),\n" +
" getTableName varchar( 32672 ),\n" +
" isAutoIncrement boolean,\n" +
" isCaseSensitive boolean,\n" +
" isCurrency boolean,\n" +
" isDefinitelyWritable boolean,\n" +
" isNullable int,\n" +
" isReadOnly boolean,\n" +
" isSearchable boolean,\n" +
" isSigned boolean,\n" +
" isWritable boolean\n" +
")\n" +
"language java parameter style derby_jdbc_result_set reads sql data\n" +
"external name 'org.apache.derbyTesting.functionTests.tests.lang.RSMDWrapper.getResultSetMetaData'\n"
);
}
if ( !tableExists( conn, "T" ) )
{
goodStatement
(
conn,
"create table t( a int, b varchar( 100 ) )"
);
goodStatement
(
conn,
"create index t_a on t( a )"
);
}
if ( !tableExists( conn, "S" ) )
{
goodStatement
(
conn,
"create table s( a int, b varchar( 100 ) )"
);
goodStatement
(
conn,
"create index s_a on s( a )"
);
}
if ( !tableExists( conn, "R" ) )
{
goodStatement
(
conn,
"create table r( a int, b varchar( 100 ) )"
);
goodStatement
(
conn,
"create index r_a on r( a )"
);
}
if ( !tableExists( conn, "T1" ) )
{
goodStatement
(
conn,
"create table t1( c1 int, c2 int, c3 int )"
);
}
if ( !tableExists( conn, "T2" ) )
{
goodStatement
(
conn,
"create table t2( c1 int, c2 int, c3 int )"
);
}
if ( !tableExists( conn, "T3" ) )
{
goodStatement
(
conn,
"create table t3( c1 int, c2 int, c3 int )"
);
}
}
///////////////////////////////////////////////////////////////////////////////////
//
// TESTS
//
///////////////////////////////////////////////////////////////////////////////////
/**
* <p>
* Test the planCost table function.
* </p>
*/
public void test_01_planCost() throws Exception
{
Connection conn = getConnection();
File traceFile = SupportFilesSetup.getReadWrite( TRACE_FILE_NAME );
SupportFilesSetup.deleteFile( traceFile );
// turn on xml-based optimizer tracing and run some queries
goodStatement
(
conn,
"call syscs_util.syscs_register_tool( 'optimizerTracing', true, 'xml' )"
);
// 2-table query
goodStatement
(
conn,
"select s.a from t, s where t.a = s.a"
);
// 3-table query
goodStatement
(
conn,
"select s.a from t, s, r where t.a = s.a and s.a = r.a"
);
// query involving a table function
goodStatement
(
conn,
"select s.a from s, table( integerList() ) i where s.a = i.a"
);
// turn off optimizer tracing and dump the xml trace to a file
goodStatement
(
conn,
"call syscs_util.syscs_register_tool( 'optimizerTracing', false, '" + traceFile.getPath() + "' )"
);
// install the planCost table function and view
goodStatement
(
conn,
"call syscs_util.syscs_register_tool( 'optimizerTracingViews', true, '" + traceFile.getPath() + "' )"
);
// verify the full signature of the planCost table function
assertResults
(
conn,
"select getColumnName, getColumnTypeName, getPrecision from table( getResultSetMetaData( 'select * from planCost where 1=2' ) ) g",
new String[][]
{
{ "TEXT", "VARCHAR", "32672" },
{ "STMTID", "INTEGER", "10" },
{ "QBID", "INTEGER", "10" },
{ "COMPLETE", "BOOLEAN", "1" },
{ "SUMMARY", "VARCHAR", "32672" },
{ "TYPE", "VARCHAR", "50" },
{ "ESTIMATEDCOST", "DOUBLE", "15" },
{ "ESTIMATEDROWCOUNT", "BIGINT", "19" },
},
false
);
// verify some contents of the xml output which we hope will remain stable
// across test platforms
assertResults
(
conn,
"select distinct stmtID, summary from planCost where complete order by stmtID, summary",
new String[][]
{
{ "1", "( \"APP\".\"S_A\" # \"APP\".\"T_A\" )" },
{ "1", "( \"APP\".\"T_A\" # \"APP\".\"S_A\" )" },
{ "2", "( ( \"APP\".\"R_A\" # \"APP\".\"S_A\" ) * \"APP\".\"T_A\" )" },
{ "2", "( ( \"APP\".\"R_A\" # \"APP\".\"T_A\" ) * \"APP\".\"S_A\" )" },
{ "2", "( ( \"APP\".\"S_A\" # \"APP\".\"R_A\" ) * \"APP\".\"T_A\" )" },
{ "2", "( ( \"APP\".\"S_A\" # \"APP\".\"T_A\" ) * \"APP\".\"R_A\" )" },
{ "2", "( ( \"APP\".\"T_A\" # \"APP\".\"R_A\" ) * \"APP\".\"S_A\" )" },
{ "2", "( ( \"APP\".\"T_A\" # \"APP\".\"S_A\" ) * \"APP\".\"R_A\" )" },
{ "3", "( \"APP\".\"INTEGERLIST\"() # \"APP\".\"S_A\" )" },
{ "3", "( \"APP\".\"S_A\" # \"APP\".\"INTEGERLIST\"() )" },
},
false
);
// uninstall the planCost table function and view
goodStatement
(
conn,
"call syscs_util.syscs_register_tool( 'optimizerTracingViews', false )"
);
// use planCost to examine an outer join
vetOuterJoin( conn );
// verify that you can't overwrite an existing file with xml output (DERBY-6635)
goodStatement
(
conn,
"call syscs_util.syscs_register_tool( 'optimizerTracing', true, 'xml' )"
);
expectExecutionError
(
conn,
FILE_EXISTS,
"call syscs_util.syscs_register_tool( 'optimizerTracing', false, '" + traceFile.getPath() + "' )"
);
goodStatement
(
conn,
"call syscs_util.syscs_register_tool( 'optimizerTracing', false )"
);
}
/**
* <p>
* Some general tests for XmlVTI.
* </p>
*/
public void test_02_xmlVTI() throws Exception
{
Connection conn = getConnection();
File traceFile = SupportFilesSetup.getReadOnly( SAVED_TRACE_NAME );
URL traceURL = SupportFilesSetup.getReadOnlyURL( SAVED_TRACE_NAME );
String[][] resultsParentAndChild = new String[][]
{
{ "1", "R_A", "HASH", "20.1395", "6" },
{ "1", "R_A", "NESTEDLOOP", "20.039500000000004", "6" },
{ "1", "S_A", "HASH", "20.1395", "6" },
{ "1", "S_A", "NESTEDLOOP", "20.039500000000004", "6" },
{ "1", "T_A", "HASH", "20.1395", "6" },
{ "1", "T_A", "NESTEDLOOP", "20.039500000000004", "6" },
};
String[][] resultsChildOnly = new String[][]
{
{ "R_A", "HASH", "20.1395", "6" },
{ "R_A", "NESTEDLOOP", "20.039500000000004", "6" },
{ "S_A", "HASH", "20.1395", "6" },
{ "S_A", "NESTEDLOOP", "20.039500000000004", "6" },
{ "T_A", "HASH", "20.1395", "6" },
{ "T_A", "NESTEDLOOP", "20.039500000000004", "6" },
};
// create the type and factory function needed by the XmlVTI
goodStatement
(
conn,
"create type ArrayList external name 'java.util.ArrayList' language java"
);
goodStatement
(
conn,
"create function asList( cell varchar( 32672 ) ... ) returns ArrayList\n" +
"language java parameter style derby no sql\n" +
"external name 'org.apache.derby.vti.XmlVTI.asList'\n"
);
// create an XmlVTI which reads from a file and incorporates parent tags
goodStatement
(
conn,
"create function decorationWithParentInfo\n" +
"(\n" +
" fileName varchar( 32672 ),\n" +
" rowTag varchar( 32672 ),\n" +
" parentTags ArrayList,\n" +
" childTags ArrayList\n" +
")\n" +
"returns table\n" +
"(\n" +
" qbID int,\n" +
" conglomerateName varchar( 36 ),\n" +
" joinStrategy varchar( 20 ),\n" +
" estimatedCost double,\n" +
" estimatedRowCount int\n" +
")\n" +
"language java parameter style derby_jdbc_result_set no sql\n" +
"external name 'org.apache.derby.vti.XmlVTI.xmlVTI'\n"
);
goodStatement
(
conn,
"create view decorationWithParentInfo as\n" +
"select * from table\n" +
"(\n" +
" decorationWithParentInfo\n" +
" (\n" +
" '" + traceFile.getPath() + "',\n" +
" 'decoration',\n" +
" asList( 'qbID' ),\n" +
" asList( 'decConglomerateName', 'decJoinStrategy', 'ceEstimatedCost', 'ceEstimatedRowCount' )\n" +
" )\n" +
") v\n"
);
// create an XmlVTI which reads from a file and only used child tags
goodStatement
(
conn,
"create function decorationChildOnly\n" +
"(\n" +
" fileName varchar( 32672 ),\n" +
" rowTag varchar( 32672 ),\n" +
" childTags varchar( 32672 )...\n" +
")\n" +
"returns table\n" +
"(\n" +
" conglomerateName varchar( 36 ),\n" +
" joinStrategy varchar( 20 ),\n" +
" estimatedCost double,\n" +
" estimatedRowCount int\n" +
")\n" +
"language java parameter style derby_jdbc_result_set no sql\n" +
"external name 'org.apache.derby.vti.XmlVTI.xmlVTI'\n"
);
goodStatement
(
conn,
"create view decorationChildOnly as\n" +
"select * from table\n" +
"(\n" +
" decorationChildOnly\n" +
" (\n" +
" '" + traceFile.getPath() + "',\n" +
" 'decoration',\n" +
" 'decConglomerateName', 'decJoinStrategy', 'ceEstimatedCost', 'ceEstimatedRowCount'\n" +
" )\n" +
") v\n"
);
// create an XmlVTI which reads from an url file and uses parent tags
goodStatement
(
conn,
"create function decorationURLParentInfo\n" +
"(\n" +
" urlString varchar( 32672 ),\n" +
" rowTag varchar( 32672 ),\n" +
" parentTags ArrayList,\n" +
" childTags ArrayList\n" +
")\n" +
"returns table\n" +
"(\n" +
" qbID int,\n" +
" conglomerateName varchar( 36 ),\n" +
" joinStrategy varchar( 20 ),\n" +
" estimatedCost double,\n" +
" estimatedRowCount int\n" +
")\n" +
"language java parameter style derby_jdbc_result_set no sql\n" +
"external name 'org.apache.derby.vti.XmlVTI.xmlVTIFromURL'\n"
);
goodStatement
(
conn,
"create view decorationURLParentInfo as\n" +
"select * from table\n" +
"(\n" +
" decorationURLParentInfo\n" +
" (\n" +
" '" + traceURL.toString() + "',\n" +
" 'decoration',\n" +
" asList( 'qbID' ),\n" +
" asList( 'decConglomerateName', 'decJoinStrategy', 'ceEstimatedCost', 'ceEstimatedRowCount' )\n" +
" )\n" +
") v\n"
);
// create an XmlVTI which reads from an url file and uses only child tags
goodStatement
(
conn,
"create function decorationURLChildOnly\n" +
"(\n" +
" urlString varchar( 32672 ),\n" +
" rowTag varchar( 32672 ),\n" +
" childTags varchar( 32672 )...\n" +
")\n" +
"returns table\n" +
"(\n" +
" conglomerateName varchar( 36 ),\n" +
" joinStrategy varchar( 20 ),\n" +
" estimatedCost double,\n" +
" estimatedRowCount int\n" +
")\n" +
"language java parameter style derby_jdbc_result_set no sql\n" +
"external name 'org.apache.derby.vti.XmlVTI.xmlVTIFromURL'\n"
);
goodStatement
(
conn,
"create view decorationURLChildOnly as\n" +
"select * from table\n" +
"(\n" +
" decorationURLChildOnly\n" +
" (\n" +
" '" + traceURL.toString() + "',\n" +
" 'decoration',\n" +
" 'decConglomerateName', 'decJoinStrategy', 'ceEstimatedCost', 'ceEstimatedRowCount'\n" +
" )\n" +
") v\n"
);
// verify that the XmlVTIs work
assertResults
(
conn,
"select distinct qbID, conglomerateName, joinStrategy, estimatedCost, estimatedRowCount\n" +
"from decorationWithParentInfo\n" +
"where conglomerateName like '%_A' and estimatedCost is not null\n" +
"order by qbID, conglomerateName, joinStrategy, estimatedCost, estimatedRowCount\n",
resultsParentAndChild,
false
);
assertResults
(
conn,
"select distinct conglomerateName, joinStrategy, estimatedCost, estimatedRowCount\n" +
"from decorationChildOnly\n" +
"where conglomerateName like '%_A' and estimatedCost is not null\n" +
"order by conglomerateName, joinStrategy, estimatedCost, estimatedRowCount\n",
resultsChildOnly,
false
);
assertResults
(
conn,
"select distinct qbID, conglomerateName, joinStrategy, estimatedCost, estimatedRowCount\n" +
"from decorationURLParentInfo\n" +
"where conglomerateName like '%_A' and estimatedCost is not null\n" +
"order by qbID, conglomerateName, joinStrategy, estimatedCost, estimatedRowCount\n",
resultsParentAndChild,
false
);
assertResults
(
conn,
"select distinct conglomerateName, joinStrategy, estimatedCost, estimatedRowCount\n" +
"from decorationURLChildOnly\n" +
"where conglomerateName like '%_A' and estimatedCost is not null\n" +
"order by conglomerateName, joinStrategy, estimatedCost, estimatedRowCount\n",
resultsChildOnly,
false
);
// clean up after ourselves
goodStatement( conn, "drop view decorationURLChildOnly" );
goodStatement( conn, "drop function decorationURLChildOnly" );
goodStatement( conn, "drop view decorationURLParentInfo" );
goodStatement( conn, "drop function decorationURLParentInfo" );
goodStatement( conn, "drop view decorationChildOnly" );
goodStatement( conn, "drop function decorationChildOnly" );
goodStatement( conn, "drop view decorationWithParentInfo" );
goodStatement( conn, "drop function decorationWithParentInfo" );
goodStatement( conn, "drop function asList" );
goodStatement( conn, "drop type ArrayList restrict" );
}
/**
* <p>
* Test xml optimizer tracing of outer joins.
* </p>
*/
private void vetOuterJoin( Connection conn ) throws Exception
{
File traceFile = SupportFilesSetup.getReadWrite( TRACE_FILE_NAME );
SupportFilesSetup.deleteFile( traceFile );
// turn on xml-based optimizer tracing
goodStatement
(
conn,
"call syscs_util.syscs_register_tool( 'optimizerTracing', true, 'xml' )"
);
// run an outer join
goodStatement
(
conn,
"select * from t3, (t1 left outer join t2 on t1.c1 = t2.c1) where t3.c1 = t1.c1"
);
// turn off optimizer tracing
goodStatement
(
conn,
"call syscs_util.syscs_register_tool( 'optimizerTracing', false, '" + traceFile.getPath() + "' )"
);
// load the trace viewer
goodStatement
(
conn,
"call syscs_util.syscs_register_tool( 'optimizerTracingViews', true, '" + traceFile.getPath() + "' )"
);
// verify the plan shapes which were considered
PreparedStatement ps = chattyPrepare
(
conn,
"select distinct summary from planCost\n" +
"where complete and qbID = 1\n" +
"order by summary\n"
);
ResultSet rs = ps.executeQuery();
rs.next();
String summary1 = rs.getString( 1 ).trim();
rs.next();
String summary2 = rs.getString( 1 ).trim();
assertTrue( summary1.startsWith( "( \"APP\"." ) );
assertTrue( summary1.endsWith( " * ProjectRestrictNode )" ) );
assertTrue( summary2.startsWith( "( ProjectRestrictNode # \"APP\"." ) );
rs.close();
ps.close();
// unload the trace viewer
goodStatement
(
conn,
"call syscs_util.syscs_register_tool( 'optimizerTracingViews', false )"
);
}
///////////////////////////////////////////////////////////////////////////////////
//
// MINIONS
//
///////////////////////////////////////////////////////////////////////////////////
/** Return true if the SQL routine exists */
private boolean routineExists( Connection conn, String functionName ) throws Exception
{
PreparedStatement ps = chattyPrepare( conn, "select count (*) from sys.sysaliases where alias = ?" );
ps.setString( 1, functionName );
ResultSet rs = ps.executeQuery();
rs.next();
boolean retval = rs.getInt( 1 ) > 0 ? true : false;
rs.close();
ps.close();
return retval;
}
/** Return true if the table exists */
private boolean tableExists( Connection conn, String tableName ) throws Exception
{
PreparedStatement ps = chattyPrepare( conn, "select count (*) from sys.systables where tablename = ?" );
ps.setString( 1, tableName );
ResultSet rs = ps.executeQuery();
rs.next();
boolean retval = rs.getInt( 1 ) > 0 ? true : false;
rs.close();
ps.close();
return retval;
}
}