[
https://issues.apache.org/jira/browse/DERBY-6211?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Rick Hillegas updated DERBY-6211:
---------------------------------
Attachment: derby-6211-12-aa-traceEndOfQueryBlock.diff
Attaching derby-6211-12-aa-traceEndOfQueryBlock.diff. This patch makes a couple
improvements to the xml-based optimizer tracing of outer join queries. Tests
passed cleanly for me on this patch.
The optimization of outer joins is interesting. In some cases, an outer join
can be turned into an inner join and optimized as a single query block.
However, in other cases, the tables in the outer join cannot be placed into a
master join order with the other tables in the query. The following query
behaves as an inner join because the compiler sees that the "t3.c1 = t2.c1"
qualification will only be satisfied for rows in which t2 inner joined to t1
and therefore t2's columns are not null:
a)
select * from t3, (t1 left outer join t2 on t1.c1 = t2.c1) where t3.c1 =
t2.c1;
However, the following query may qualify rows in which t2 outer joined to t1
and t2's columns are null. The following query cannot be turned into one big
inner join:
b)
select * from t3, (t1 left outer join t2 on t1.c1 = t2.c1) where t3.c1 =
t1.c1;
For query (b), the whole "(t1 left outer join t2 on t1.c1 = t2.c1)" clause is
treated as a separate query block. The situation is actually even more
complicated than that, and we'll get to that later. But at a high level, the
optimizer starts out considering two join orders:
[t3, QueryBock2]
and
[QueryBlock2, t3]
While evaluating these join orders, the optimizer recurses and optimizes
QueryBlock2. As you can see, QueryBlock2 will be optimized twice.
This situation broke xml-based optimizer tracing in several ways:
1) The xml tracer died on an NPE when trying to represent the join order and
give a compact name to QueryBlock2. The fix was to give QueryBlock2 a generic
name, viz., the class name of the query tree node which sits on top of
QueryBlock2.
2) Once that bug was fixed, it became apparent that there was no marker for the
end of the recursive optimization of QueryBlock2. This caused trace information
to be added to the wrong query block. The fix was to add some recursion to the
xml tracer and to add a new trace method which flags the end of an attempt to
optimize a query block.
3) Once that bug was fixed, another variant of problem (1) surfaced: an NPE was
raised trying to give QueryBlock2 a compact name in the query plan summary.
Again, that problem was fixed by using the class name of the query tree node
which sits on top of QueryBlock2.
As promised above, the situation is actually a bit more complicated. Remember
that QueryBlock2 represents the following clause:
(t1 left outer join t2 on t1.c1 = t2.c1)
The optimizer doesn't have many tricks for this outer join. It can't permute
the join order. t1 must be outer to t2. So, the optimizer treats this clause as
two separate query blocks and optimizes them separately. This happens because
the HalfOuterJoinNode is really a TableOperatorNode, just like a UNION. Each
branch of the HalfOuterJoinNode is treated as its own query block just as each
branch of a UNION is treated as a separate, independent query block.
I hope that this explanation makes it easier to interpret the results of the
following script. You will notice two plans considered for query (b):
( ProjectRestrictNode * "APP"."SQL130807054111430" )
( "APP"."67bb80b4-0140-58cb-8920-00000383a238" * ProjectRestrictNode )
Here's the script:
connect 'jdbc:derby:memory:db;create=true';
create table t1
(
c1 int,
c2 int,
c3 int,
constraint cons1 primary key(c1, c2)
);
create table t2
(
c1 int not null,
c2 int not null,
c3 int,
constraint cons2 unique(c1, c2)
);
create table t3
(
c1 int,
c2 int,
c3 int,
constraint cons3 primary key(c1, c2)
);
select cast( t.tablename as varchar(2)), c.conglomeratename
from sys.systables t, sys.sysconglomerates c
where t.tableid = c.tableid
and t.tabletype = 'T'
order by t.tablename;
--turn on xml-based optimizer tracing
call syscs_util.syscs_register_tool( 'optimizerTracing', true, 'xml' );
select * from t1 left outer join t2 on t1.c1 = t2.c1;
select * from t3, (t1 left outer join t2 on t1.c1 = t2.c1) where t3.c1 = t2.c1;
select * from t3, (t1 left outer join t2 on t1.c1 = t2.c1) where t3.c1 = t1.c1;
-- turn off optimizer tracing
call syscs_util.syscs_register_tool( 'optimizerTracing', false,
'optimizerTrace.xml' );
-- load the trace viewer
call syscs_util.syscs_register_tool( 'optimizerTracingViews', true,
'optimizerTrace.xml' );
-- view the costs of all complete plans
select stmtID, qbID, estimatedCost, summary from planCost
where complete
order by stmtID, qbID, estimatedCost
;
-- unload the trace viewer
call syscs_util.syscs_register_tool( 'optimizerTracingViews', false );
Touches the following files:
---------------
M java/engine/org/apache/derby/impl/sql/compile/XMLOptTrace.java
Added recursive treatment of query blocks and fixed the NPEs.
---------------
M java/engine/org/apache/derby/iapi/sql/compile/OptimizerPlan.java
Added a new kind of node in the OptimizerPlan: DeadEnd. This is a node for
cases when the xml tracer can't peer inside an Optimizable. For instance, when
the Optimizable is another query block.
---------------
M java/engine/org/apache/derby/iapi/sql/compile/OptTrace.java
M java/engine/org/apache/derby/impl/sql/compile/DefaultOptTrace.java
M java/engine/org/apache/derby/impl/sql/compile/OptimizerImpl.java
M
java/testing/org/apache/derbyTesting/functionTests/tests/lang/DummyOptTrace.java
Changed the name of the traceStart() method to traceStartQueryBlock(). Added a
new traceEndQueryBlock() method.
---------------
M java/engine/org/apache/derby/impl/sql/compile/TableOperatorNode.java
Adds a traceEndQueryBlock() call after a recursive optimization finishes.
> Make Optimizer trace logic pluggable.
> -------------------------------------
>
> Key: DERBY-6211
> URL: https://issues.apache.org/jira/browse/DERBY-6211
> Project: Derby
> Issue Type: Improvement
> Components: SQL
> Affects Versions: 10.11.0.0
> Reporter: Rick Hillegas
> Assignee: Rick Hillegas
> Labels: derby_triage10_11
> Attachments: derby-6211-01-aa-createPlugin.diff,
> derby-6211-02-aa-cleanup.diff, derby-6211-02-ab-cleanup.diff,
> derby-6211-03-aa-customTracer.diff,
> derby-6211-04-aa-moveOptimizerTracerToEngineJar.diff,
> derby-6211-05-aa-xmlOptimizerTracer.diff,
> derby-6211-06-ab-packageProtect-XMLOptTrace.diff,
> derby-6211-07-aa-useSchemaQualifiedNamesInSummaries.diff,
> derby-6211-07-ab-useSchemaQualifiedNamesInSummaries.diff,
> derby-6211-08-aa-fixNPE.diff, derby-6211-09-aa-addTests.diff,
> derby-6211-10-aa-makingCostEstimateObject.diff,
> derby-6211-11-aa-moveTracerOutOfOptimizer.diff,
> derby-6211-11-ab-moveTracerOutOfOptimizer.diff,
> derby-6211-12-aa-traceEndOfQueryBlock.diff
>
>
> Right now the trace logic in the optimizer is hard-coded to produce a stream
> of diagnostics. It would be good to be able to plug alternative trace logic
> into the optimizer. This would make the following possible:
> 1) Plug in trace logic which produces formats which are easier to study and
> which can be analyzed mechanically. E.g., xml formatted output.
> 2) Plug in trace logic which can be used during unit testing to verify that
> the optimizer has picked the right plan. Over time this might make it easier
> to migrate canon-based tests to assertion-based tests.
--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators
For more information on JIRA, see: http://www.atlassian.com/software/jira