mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-24 03:23:03 +01:00
1) Fix bug with LEFT JOIN ViewX and where clause with ViewX.FieldX IS NULL
2) When FIRST / SKIP is used in a sub-query it may not be affected by conjunctions in higher contexts. Example (should return 1 row instead of 5): SELECT * FROM (SELECT FIRST 5 SKIP 2 cs.* FROM RDB$CHARACTER_SETS cs) WHERE RDB$CHARACTER_SET_NAME starting with 'D'
This commit is contained in:
parent
5ddd2d1cb0
commit
b355d3411e
138
src/jrd/opt.cpp
138
src/jrd/opt.cpp
@ -104,6 +104,7 @@ static USHORT distribute_equalities(LLS *, Csb*, USHORT);
|
|||||||
static bool dump_index(const jrd_nod*, SCHAR**, SSHORT*);
|
static bool dump_index(const jrd_nod*, SCHAR**, SSHORT*);
|
||||||
static bool dump_rsb(const jrd_req*, const Rsb*, SCHAR**, SSHORT*);
|
static bool dump_rsb(const jrd_req*, const Rsb*, SCHAR**, SSHORT*);
|
||||||
static bool estimate_cost(thread_db*, OPT, USHORT, double *, double *);
|
static bool estimate_cost(thread_db*, OPT, USHORT, double *, double *);
|
||||||
|
static bool expression_contains(const jrd_nod*, NOD_T, bool, bool);
|
||||||
#ifdef EXPRESSION_INDICES
|
#ifdef EXPRESSION_INDICES
|
||||||
static bool expression_equal(thread_db*, jrd_nod*, jrd_nod*);
|
static bool expression_equal(thread_db*, jrd_nod*, jrd_nod*);
|
||||||
#endif
|
#endif
|
||||||
@ -180,7 +181,8 @@ static SSHORT sort_indices_by_priority(Csb::csb_repeat*, IDX **, UINT64 *);
|
|||||||
#define DEBUG_NONE 0
|
#define DEBUG_NONE 0
|
||||||
|
|
||||||
IB_FILE *opt_debug_file = 0;
|
IB_FILE *opt_debug_file = 0;
|
||||||
static int opt_debug_flag = DEBUG_NONE;
|
static int opt_debug_flag = DEBUG_RELATIONSHIPS;
|
||||||
|
//static int opt_debug_flag = DEBUG_NONE;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
inline void SET_DEP_BIT(ULONG* array, const SLONG bit)
|
inline void SET_DEP_BIT(ULONG* array, const SLONG bit)
|
||||||
@ -361,10 +363,9 @@ Rsb* OPT_compile(thread_db* tdbb,
|
|||||||
jrd_nod* project = rse->rse_projection;
|
jrd_nod* project = rse->rse_projection;
|
||||||
jrd_nod* aggregate = rse->rse_aggregate;
|
jrd_nod* aggregate = rse->rse_aggregate;
|
||||||
|
|
||||||
// put any additional booleans on the conjunct stack, and see if we
|
// put any additional booleans on the conjunct stack, and see if we
|
||||||
// can generate additional booleans by associativity--this will help
|
// can generate additional booleans by associativity--this will help
|
||||||
// to utilize indices that we might not have noticed
|
// to utilize indices that we might not have noticed
|
||||||
|
|
||||||
if (rse->rse_boolean) {
|
if (rse->rse_boolean) {
|
||||||
conjunct_count =
|
conjunct_count =
|
||||||
decompose(tdbb, rse->rse_boolean, &conjunct_stack, csb);
|
decompose(tdbb, rse->rse_boolean, &conjunct_stack, csb);
|
||||||
@ -372,19 +373,26 @@ Rsb* OPT_compile(thread_db* tdbb,
|
|||||||
|
|
||||||
conjunct_count += distribute_equalities(&conjunct_stack, csb, conjunct_count);
|
conjunct_count += distribute_equalities(&conjunct_stack, csb, conjunct_count);
|
||||||
|
|
||||||
// find the end of the conjunct stack.
|
// find the end of the conjunct stack.
|
||||||
lls** stack_end = &conjunct_stack;
|
lls** stack_end = &conjunct_stack;
|
||||||
while (*stack_end) {
|
while (*stack_end) {
|
||||||
stack_end = &(*stack_end)->lls_next;
|
stack_end = &(*stack_end)->lls_next;
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear the csb_active flag of all streams in the rse
|
// AB: If we have limit our retrieval with FIRST / SKIP syntax then
|
||||||
|
// we may not deliver above conditions (from higher rse's) to this
|
||||||
|
// rse, because the results should be consistent.
|
||||||
|
if (rse->rse_skip || rse->rse_first) {
|
||||||
|
parent_stack = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear the csb_active flag of all streams in the rse
|
||||||
set_rse_inactive(csb, rse);
|
set_rse_inactive(csb, rse);
|
||||||
|
|
||||||
UCHAR* p = streams + 1;
|
UCHAR* p = streams + 1;
|
||||||
|
|
||||||
// go through the record selection expression generating
|
// go through the record selection expression generating
|
||||||
// record source blocks for all streams
|
// record source blocks for all streams
|
||||||
|
|
||||||
// CVC: I defined this var here because it's assigned inside an if() shortly
|
// CVC: I defined this var here because it's assigned inside an if() shortly
|
||||||
// below but it's used later in the loop always, so I assume the idea is that
|
// below but it's used later in the loop always, so I assume the idea is that
|
||||||
@ -461,9 +469,40 @@ Rsb* OPT_compile(thread_db* tdbb,
|
|||||||
csb->csb_rpt[outer_streams[i]].csb_flags |= csb_active;
|
csb->csb_rpt[outer_streams[i]].csb_flags |= csb_active;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lls** stackSavepoint = &conjunct_stack;
|
||||||
|
if (rse->rse_jointype != blr_inner) {
|
||||||
|
// Make list of nodes that can be delivered to a outer-stream.
|
||||||
|
// In fact this are all nodes except IS NULL (nod_missing).
|
||||||
|
// Note! That for OR nodes this meant the whole node must be ignored.
|
||||||
|
lls** stackItem = &parent_stack;
|
||||||
|
for (; *stackItem; stackItem = &(*stackItem)->lls_next) {
|
||||||
|
jrd_nod* deliverNode = (jrd_nod*) (*stackItem)->lls_object;
|
||||||
|
if (!expression_contains(deliverNode, nod_missing, true, true)) {
|
||||||
|
LLS_PUSH(deliverNode, &conjunct_stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
*stack_end = parent_stack;
|
*stack_end = parent_stack;
|
||||||
|
}
|
||||||
|
|
||||||
rsb = OPT_compile(tdbb, csb, (RSE) node, conjunct_stack);
|
rsb = OPT_compile(tdbb, csb, (RSE) node, conjunct_stack);
|
||||||
|
|
||||||
|
if (rse->rse_jointype != blr_inner) {
|
||||||
|
if (parent_stack) {
|
||||||
|
// Remove previously added parent conjuctions from the stack.
|
||||||
|
lls** stackItem = &conjunct_stack;
|
||||||
|
for (; stackSavepoint != stackItem; stackItem = &(*stackItem)->lls_next) {
|
||||||
|
LLS_POP(&conjunct_stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
*stack_end = NULL;
|
*stack_end = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (rse->rse_jointype == blr_left) {
|
if (rse->rse_jointype == blr_left) {
|
||||||
for (SSHORT i = 1; i <= outer_streams[0]; i++) {
|
for (SSHORT i = 1; i <= outer_streams[0]; i++) {
|
||||||
csb->csb_rpt[outer_streams[i]].csb_flags &= ~csb_active;
|
csb->csb_rpt[outer_streams[i]].csb_flags &= ~csb_active;
|
||||||
@ -543,35 +582,36 @@ Rsb* OPT_compile(thread_db* tdbb,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is an attempt to make sure we have a large enough cache to
|
// this is an attempt to make sure we have a large enough cache to
|
||||||
// efficiently retrieve this query; make sure the cache has a minimum
|
// efficiently retrieve this query; make sure the cache has a minimum
|
||||||
// number of pages for each stream in the RSE (the number is just a guess)
|
// number of pages for each stream in the RSE (the number is just a guess)
|
||||||
if (streams[0] > 5) {
|
if (streams[0] > 5) {
|
||||||
CCH_expand(tdbb, (ULONG) (streams[0] * CACHE_PAGES_PER_STREAM));
|
CCH_expand(tdbb, (ULONG) (streams[0] * CACHE_PAGES_PER_STREAM));
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point we are ready to start optimizing.
|
// At this point we are ready to start optimizing.
|
||||||
// We will use the opt block to hold information of
|
// We will use the opt block to hold information of
|
||||||
// a global nature, meaning that it needs to stick
|
// a global nature, meaning that it needs to stick
|
||||||
// around for the rest of the optimization process.
|
// around for the rest of the optimization process.
|
||||||
|
|
||||||
// first fill out the conjuncts at the end of opt
|
// first fill out the conjuncts at the end of opt
|
||||||
opt_->opt_base_conjuncts = (SSHORT) conjunct_count;
|
opt_->opt_base_conjuncts = (SSHORT) conjunct_count;
|
||||||
|
|
||||||
// Check if size of optimizer block exceeded.
|
// Check if size of optimizer block exceeded.
|
||||||
if (conjunct_count > MAX_CONJUNCTS) {
|
if (conjunct_count > MAX_CONJUNCTS) {
|
||||||
ERR_post(isc_optimizer_blk_exc, 0);
|
ERR_post(isc_optimizer_blk_exc, 0);
|
||||||
// Msg442: size of optimizer block exceeded
|
// Msg442: size of optimizer block exceeded
|
||||||
}
|
}
|
||||||
|
|
||||||
// AB: Because know we're going to use both parent & conjunct_stack
|
// AB: Because know we're going to use both parent & conjunct_stack
|
||||||
// try to again to make additional conjuncts for an inner join,
|
// try to again to make additional conjuncts for an inner join,
|
||||||
// but be sure that opt_->opt_base_conjuncts contains the real "base"
|
// but be sure that opt_->opt_base_conjuncts contains the real "base"
|
||||||
|
|
||||||
// find the end of the conjunct stack.
|
// find the end of the conjunct stack.
|
||||||
for (stack_end = &conjunct_stack; *stack_end;
|
for (stack_end = &conjunct_stack; *stack_end;
|
||||||
stack_end = &(*stack_end)->lls_next);
|
stack_end = &(*stack_end)->lls_next);
|
||||||
const SLONG saved_conjunct_count = conjunct_count;
|
const SLONG saved_conjunct_count = conjunct_count;
|
||||||
|
|
||||||
*stack_end = parent_stack;
|
*stack_end = parent_stack;
|
||||||
conjunct_count += distribute_equalities(&conjunct_stack, csb, conjunct_count);
|
conjunct_count += distribute_equalities(&conjunct_stack, csb, conjunct_count);
|
||||||
if (parent_stack) {
|
if (parent_stack) {
|
||||||
@ -590,7 +630,7 @@ Rsb* OPT_compile(thread_db* tdbb,
|
|||||||
|
|
||||||
SSHORT i;
|
SSHORT i;
|
||||||
|
|
||||||
// AB: If equality nodes could be made get them first from the stack
|
// AB: If equality nodes could be made get them first from the stack
|
||||||
for (i = saved_conjunct_count; i < conjunct_count; i++) {
|
for (i = saved_conjunct_count; i < conjunct_count; i++) {
|
||||||
jrd_nod* node = (jrd_nod*) LLS_POP(&conjunct_stack);
|
jrd_nod* node = (jrd_nod*) LLS_POP(&conjunct_stack);
|
||||||
opt_->opt_conjuncts[i].opt_conjunct_node = node;
|
opt_->opt_conjuncts[i].opt_conjunct_node = node;
|
||||||
@ -603,8 +643,8 @@ Rsb* OPT_compile(thread_db* tdbb,
|
|||||||
compute_dependencies(node, opt_->opt_conjuncts[i].opt_dependencies);
|
compute_dependencies(node, opt_->opt_conjuncts[i].opt_dependencies);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the conjuncts from the parent rse. But don't fiddle with
|
// Store the conjuncts from the parent rse. But don't fiddle with
|
||||||
// the parent's stack itself.
|
// the parent's stack itself.
|
||||||
for (; parent_stack && conjunct_count < MAX_CONJUNCTS;
|
for (; parent_stack && conjunct_count < MAX_CONJUNCTS;
|
||||||
parent_stack = parent_stack->lls_next, conjunct_count++)
|
parent_stack = parent_stack->lls_next, conjunct_count++)
|
||||||
{
|
{
|
||||||
@ -615,13 +655,13 @@ Rsb* OPT_compile(thread_db* tdbb,
|
|||||||
opt_->opt_conjuncts[conjunct_count].opt_dependencies);
|
opt_->opt_conjuncts[conjunct_count].opt_dependencies);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if size of optimizer block exceeded.
|
// Check if size of optimizer block exceeded.
|
||||||
if (parent_stack) {
|
if (parent_stack) {
|
||||||
ERR_post(isc_optimizer_blk_exc, 0);
|
ERR_post(isc_optimizer_blk_exc, 0);
|
||||||
// Msg442: size of optimizer block exceeded
|
// Msg442: size of optimizer block exceeded
|
||||||
}
|
}
|
||||||
|
|
||||||
// attempt to optimize aggregates via an index, if possible
|
// attempt to optimize aggregates via an index, if possible
|
||||||
if (aggregate && !sort && !project) {
|
if (aggregate && !sort && !project) {
|
||||||
sort = aggregate;
|
sort = aggregate;
|
||||||
}
|
}
|
||||||
@ -629,12 +669,12 @@ Rsb* OPT_compile(thread_db* tdbb,
|
|||||||
rse->rse_aggregate = aggregate = NULL;
|
rse->rse_aggregate = aggregate = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// AB: Mark the previous used streams (sub-rse's) as active
|
// AB: Mark the previous used streams (sub-rse's) as active
|
||||||
for (i = 1; i <= sub_streams[0]; i++) {
|
for (i = 1; i <= sub_streams[0]; i++) {
|
||||||
csb->csb_rpt[sub_streams[i]].csb_flags |= csb_active;
|
csb->csb_rpt[sub_streams[i]].csb_flags |= csb_active;
|
||||||
}
|
}
|
||||||
|
|
||||||
// outer joins require some extra processing
|
// outer joins require some extra processing
|
||||||
if (rse->rse_jointype != blr_inner) {
|
if (rse->rse_jointype != blr_inner) {
|
||||||
rsb = gen_outer(tdbb, opt_, rse, rivers_stack, &sort, &project);
|
rsb = gen_outer(tdbb, opt_, rse, rivers_stack, &sort, &project);
|
||||||
}
|
}
|
||||||
@ -2675,6 +2715,46 @@ static bool estimate_cost(thread_db* tdbb,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool expression_contains(const jrd_nod* node, NOD_T node_type, bool go_into_or, bool go_into_and)
|
||||||
|
{
|
||||||
|
/**************************************
|
||||||
|
*
|
||||||
|
* e x p r e s s i o n _ c o n t a i n s
|
||||||
|
*
|
||||||
|
**************************************
|
||||||
|
*
|
||||||
|
* Functional description
|
||||||
|
* Search if somewhere in the expression the give
|
||||||
|
* node_type is burried.
|
||||||
|
*
|
||||||
|
**************************************/
|
||||||
|
DEV_BLKCHK(node, type_nod);
|
||||||
|
|
||||||
|
if (!node) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node->nod_type == node_type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
if (go_into_or && (node->nod_type == nod_or)) {
|
||||||
|
return expression_contains(node->nod_arg[0], node_type, go_into_or, go_into_and) ||
|
||||||
|
expression_contains(node->nod_arg[1], node_type, go_into_or, go_into_and);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (go_into_and && (node->nod_type == nod_and)) {
|
||||||
|
return expression_contains(node->nod_arg[0], node_type, go_into_or, go_into_and) ||
|
||||||
|
expression_contains(node->nod_arg[1], node_type, go_into_or, go_into_and);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef EXPRESSION_INDICES
|
#ifdef EXPRESSION_INDICES
|
||||||
static bool expression_equal(thread_db* tdbb, jrd_nod* node1, jrd_nod* node2)
|
static bool expression_equal(thread_db* tdbb, jrd_nod* node1, jrd_nod* node2)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user