mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-23 22:43:04 +01:00
Fixed CORE-1246 - Incorrect column values with outer joins and derived tables
This commit is contained in:
parent
10562bbcdf
commit
b550749a47
@ -654,7 +654,8 @@ class dsql_ctx : public pool_alloc<dsql_type_ctx>
|
||||
{
|
||||
public:
|
||||
explicit dsql_ctx(MemoryPool &p)
|
||||
: ctx_childs_derived_table(p),
|
||||
: ctx_main_derived_contexts(p),
|
||||
ctx_childs_derived_table(p),
|
||||
ctx_imp_join(p)
|
||||
{
|
||||
}
|
||||
@ -672,6 +673,8 @@ public:
|
||||
USHORT ctx_recursive; //!< Secondary context id for recursive UNION (nobody referred to this context)
|
||||
USHORT ctx_scope_level; //!< Subquery level within this request
|
||||
USHORT ctx_flags; //!< Various flag values
|
||||
USHORT ctx_in_outer_join; // req_in_outer_join when context was created
|
||||
DsqlContextStack ctx_main_derived_contexts; // contexts used for blr_derived_expr
|
||||
DsqlContextStack ctx_childs_derived_table; //!< Childs derived table context
|
||||
Firebird::GenericMap<Firebird::Pair<Firebird::Left<
|
||||
Firebird::MetaName, ImplicitJoin*> > > ctx_imp_join; // Map of USING fieldname to ImplicitJoin
|
||||
@ -690,6 +693,8 @@ public:
|
||||
ctx_recursive = v.ctx_recursive;
|
||||
ctx_scope_level = v.ctx_scope_level;
|
||||
ctx_flags = v.ctx_flags;
|
||||
ctx_in_outer_join = v.ctx_in_outer_join;
|
||||
ctx_main_derived_contexts.assign(v.ctx_main_derived_contexts);
|
||||
ctx_childs_derived_table.assign(v.ctx_childs_derived_table);
|
||||
ctx_imp_join.assign(v.ctx_imp_join);
|
||||
|
||||
|
@ -171,6 +171,34 @@ void GEN_expr(CompiledStatement* statement, dsql_nod* node)
|
||||
return;
|
||||
|
||||
case nod_derived_field:
|
||||
// ASF: If we are not referencing a field, we should evaluate the expression based on
|
||||
// a set (ORed) of contexts. If any of them are in a valid position the expression is
|
||||
// evaluated, otherwise a NULL will be returned. This is fix for CORE-1246.
|
||||
if (node->nod_arg[e_derived_field_value]->nod_type != nod_field &&
|
||||
node->nod_arg[e_derived_field_value]->nod_type != nod_dbkey &&
|
||||
node->nod_arg[e_derived_field_value]->nod_type != nod_map)
|
||||
{
|
||||
dsql_ctx* ctx = (dsql_ctx*) node->nod_arg[e_derived_field_context];
|
||||
|
||||
if (ctx->ctx_main_derived_contexts.hasData())
|
||||
{
|
||||
if (ctx->ctx_main_derived_contexts.getCount() > MAX_UCHAR)
|
||||
{
|
||||
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
|
||||
Arg::Gds(isc_imp_exc) <<
|
||||
Arg::Gds(isc_ctx_too_big));
|
||||
}
|
||||
|
||||
stuff(statement, blr_derived_expr);
|
||||
stuff(statement, ctx->ctx_main_derived_contexts.getCount());
|
||||
|
||||
for (DsqlContextStack::iterator stack(ctx->ctx_main_derived_contexts);
|
||||
stack.hasData(); ++stack)
|
||||
{
|
||||
stuff(statement, stack.object()->ctx_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
GEN_expr(statement, node->nod_arg[e_derived_field_value]);
|
||||
return;
|
||||
|
||||
|
@ -2005,6 +2005,9 @@ static void make_parameter_names(dsql_par* parameter, const dsql_nod* item)
|
||||
case nod_agg_list:
|
||||
name_alias = "LIST";
|
||||
break;
|
||||
case nod_constant:
|
||||
name_alias = "CONSTANT";
|
||||
break;
|
||||
} // switch(map_node->nod_type)
|
||||
break;
|
||||
} // case nod_map
|
||||
|
@ -418,6 +418,7 @@ dsql_ctx* PASS1_make_context(CompiledStatement* statement, const dsql_nod* relat
|
||||
if (statement->req_in_outer_join) {
|
||||
context->ctx_flags |= CTX_outer_join;
|
||||
}
|
||||
context->ctx_in_outer_join = statement->req_in_outer_join;
|
||||
|
||||
// find the context alias name, if it exists.
|
||||
dsql_str* string;
|
||||
@ -4837,18 +4838,38 @@ static dsql_nod* pass1_derived_table(CompiledStatement* statement, dsql_nod* inp
|
||||
rse = PASS1_rse(statement, select_expr, NULL);
|
||||
}
|
||||
|
||||
USHORT minOuterJoin = MAX_USHORT;
|
||||
|
||||
// Finish off by cleaning up contexts and put them into req_dt_context
|
||||
// so create view (ddl) can deal with it.
|
||||
// Also add the used contexts into the childs stack.
|
||||
while (temp.hasData() && (temp.object() != baseContext))
|
||||
{
|
||||
statement->req_dt_context.push(temp.object());
|
||||
// Collect contexts that will be used for blr_derived_expr generation.
|
||||
// We want all child contexts with minimum ctx_in_outer_join.
|
||||
dsql_ctx* childCtx = temp.object();
|
||||
|
||||
if (childCtx->ctx_in_outer_join <= minOuterJoin &&
|
||||
(childCtx->ctx_relation || childCtx->ctx_procedure))
|
||||
{
|
||||
if (childCtx->ctx_parent)
|
||||
childCtx = childCtx->ctx_parent;
|
||||
|
||||
if (childCtx->ctx_in_outer_join < minOuterJoin)
|
||||
{
|
||||
minOuterJoin = childCtx->ctx_in_outer_join;
|
||||
context->ctx_main_derived_contexts.clear();
|
||||
}
|
||||
|
||||
context->ctx_main_derived_contexts.push(childCtx);
|
||||
}
|
||||
|
||||
statement->req_dt_context.push(childCtx);
|
||||
context->ctx_childs_derived_table.push(temp.pop());
|
||||
}
|
||||
|
||||
while (temp.hasData())
|
||||
{
|
||||
temp.pop();
|
||||
}
|
||||
}
|
||||
context->ctx_rse = node->nod_arg[e_derived_table_rse] = rse;
|
||||
|
||||
@ -6430,7 +6451,7 @@ static dsql_nod* pass1_join(CompiledStatement* statement, dsql_nod* input)
|
||||
PASS1_node(statement, input->nod_arg[e_join_left_rel]);
|
||||
node->nod_arg[e_join_rght_rel] =
|
||||
PASS1_node(statement, input->nod_arg[e_join_rght_rel]);
|
||||
break;
|
||||
break;
|
||||
case nod_join_left:
|
||||
node->nod_arg[e_join_left_rel] =
|
||||
PASS1_node(statement, input->nod_arg[e_join_left_rel]);
|
||||
@ -6438,7 +6459,7 @@ static dsql_nod* pass1_join(CompiledStatement* statement, dsql_nod* input)
|
||||
node->nod_arg[e_join_rght_rel] =
|
||||
PASS1_node(statement, input->nod_arg[e_join_rght_rel]);
|
||||
statement->req_in_outer_join--;
|
||||
break;
|
||||
break;
|
||||
case nod_join_right:
|
||||
statement->req_in_outer_join++;
|
||||
node->nod_arg[e_join_left_rel] =
|
||||
@ -6446,7 +6467,7 @@ static dsql_nod* pass1_join(CompiledStatement* statement, dsql_nod* input)
|
||||
statement->req_in_outer_join--;
|
||||
node->nod_arg[e_join_rght_rel] =
|
||||
PASS1_node(statement, input->nod_arg[e_join_rght_rel]);
|
||||
break;
|
||||
break;
|
||||
case nod_join_full:
|
||||
statement->req_in_outer_join++;
|
||||
node->nod_arg[e_join_left_rel] =
|
||||
@ -6454,11 +6475,11 @@ static dsql_nod* pass1_join(CompiledStatement* statement, dsql_nod* input)
|
||||
node->nod_arg[e_join_rght_rel] =
|
||||
PASS1_node(statement, input->nod_arg[e_join_rght_rel]);
|
||||
statement->req_in_outer_join--;
|
||||
break;
|
||||
break;
|
||||
|
||||
default:
|
||||
fb_assert(false); // join type expected
|
||||
break;
|
||||
break;
|
||||
}
|
||||
|
||||
// Process boolean
|
||||
@ -10036,6 +10057,9 @@ static dsql_nod* remap_field(CompiledStatement* statement, dsql_nod* field,
|
||||
field->nod_arg[e_hidden_var_expr], context, current_level);
|
||||
return field;
|
||||
|
||||
case nod_constant:
|
||||
return post_map(field, context);
|
||||
|
||||
default:
|
||||
return field;
|
||||
}
|
||||
|
@ -141,22 +141,44 @@ bool OPT_computable(CompilerScratch* csb, const jrd_nod* node, SSHORT stream,
|
||||
|
||||
case nod_rec_version:
|
||||
case nod_dbkey:
|
||||
|
||||
n = (USHORT)(IPTR) node->nod_arg[0];
|
||||
if (allowOnlyCurrentStream) {
|
||||
if (n != stream &&
|
||||
!(csb->csb_rpt[n].csb_flags & csb_sub_stream))
|
||||
{
|
||||
if (allowOnlyCurrentStream)
|
||||
{
|
||||
if (n != stream && !(csb->csb_rpt[n].csb_flags & csb_sub_stream))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (n == stream) {
|
||||
else
|
||||
{
|
||||
if (n == stream)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return csb->csb_rpt[n].csb_flags & csb_active;
|
||||
|
||||
case nod_derived_expr:
|
||||
{
|
||||
UCHAR streamCount = (UCHAR)(IPTR) node->nod_arg[e_derived_expr_stream_count];
|
||||
USHORT* streamList = (USHORT*) node->nod_arg[e_derived_expr_stream_list];
|
||||
bool active = true;
|
||||
|
||||
for (UCHAR i = 0; i < streamCount; ++i)
|
||||
{
|
||||
n = streamList[i];
|
||||
if (allowOnlyCurrentStream)
|
||||
{
|
||||
if (n != stream && !(csb->csb_rpt[n].csb_flags & csb_sub_stream))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (n == stream)
|
||||
return false;
|
||||
}
|
||||
|
||||
active = active && (csb->csb_rpt[n].csb_flags & csb_active);
|
||||
}
|
||||
return active;
|
||||
}
|
||||
|
||||
case nod_min:
|
||||
case nod_max:
|
||||
case nod_average:
|
||||
@ -1068,11 +1090,26 @@ void OptimizerRetrieval::findDependentFromStreams(jrd_nod* node,
|
||||
case nod_dbkey:
|
||||
{
|
||||
int keyStream = (USHORT)(IPTR) node->nod_arg[0];
|
||||
if (keyStream != stream &&
|
||||
(csb->csb_rpt[keyStream].csb_flags & csb_active))
|
||||
if (keyStream != stream && (csb->csb_rpt[keyStream].csb_flags & csb_active))
|
||||
{
|
||||
if (!streamList->exist(keyStream)) {
|
||||
if (!streamList->exist(keyStream))
|
||||
streamList->add(keyStream);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
case nod_derived_expr:
|
||||
{
|
||||
UCHAR derivedStreamCount = (UCHAR)(IPTR) node->nod_arg[e_derived_expr_stream_count];
|
||||
USHORT* derivedStreamList = (USHORT*) node->nod_arg[e_derived_expr_stream_list];
|
||||
|
||||
for (UCHAR i = 0; i < derivedStreamCount; ++i)
|
||||
{
|
||||
int keyStream = derivedStreamList[i];
|
||||
if (keyStream != stream && (csb->csb_rpt[keyStream].csb_flags & csb_active))
|
||||
{
|
||||
if (!streamList->exist(keyStream))
|
||||
streamList->add(keyStream);
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
@ -226,6 +226,7 @@ static const struct
|
||||
{"similar", similar},
|
||||
{"exec_stmt", exec_stmt},
|
||||
{"stmt_expr", two},
|
||||
{"derived_expr", derived_expr},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
|
@ -373,5 +373,6 @@
|
||||
#define blr_exec_stmt_out_params (unsigned char) 13 // output parameters
|
||||
|
||||
#define blr_stmt_expr (unsigned char) 190
|
||||
#define blr_derived_expr (unsigned char) 191
|
||||
|
||||
#endif // JRD_BLR_H
|
||||
|
@ -1929,6 +1929,10 @@ void CMP_get_desc(thread_db* tdbb, CompilerScratch* csb, jrd_nod* node, DSC * de
|
||||
CMP_get_desc(tdbb, csb, node->nod_arg[e_stmt_expr_expr], desc);
|
||||
return;
|
||||
|
||||
case nod_derived_expr:
|
||||
CMP_get_desc(tdbb, csb, node->nod_arg[e_derived_expr_expr], desc);
|
||||
return;
|
||||
|
||||
default:
|
||||
fb_assert(false);
|
||||
break;
|
||||
@ -2591,7 +2595,7 @@ static jrd_nod* catenate_nodes(thread_db* tdbb, NodeStack& stack)
|
||||
static jrd_nod* copy(thread_db* tdbb,
|
||||
CompilerScratch* csb,
|
||||
jrd_nod* input,
|
||||
UCHAR * remap,
|
||||
UCHAR* remap,
|
||||
USHORT field_id,
|
||||
jrd_nod* message,
|
||||
bool remap_fld)
|
||||
@ -2727,6 +2731,32 @@ static jrd_nod* copy(thread_db* tdbb,
|
||||
return temp_node;
|
||||
}
|
||||
|
||||
case nod_derived_expr:
|
||||
{
|
||||
node = PAR_make_node(tdbb, e_derived_expr_length);
|
||||
node->nod_count = e_derived_expr_count;
|
||||
node->nod_type = input->nod_type;
|
||||
node->nod_arg[e_derived_expr_expr] = copy(tdbb, csb, input->nod_arg[e_derived_expr_expr],
|
||||
remap, field_id, message, remap_fld);
|
||||
|
||||
if (remap)
|
||||
{
|
||||
UCHAR streamCount = (UCHAR)(IPTR) input->nod_arg[e_derived_expr_stream_count];
|
||||
USHORT* oldStreamList = (USHORT*) input->nod_arg[e_derived_expr_stream_list];
|
||||
USHORT* newStreamList = FB_NEW(*tdbb->getDefaultPool()) USHORT[streamCount];
|
||||
|
||||
for (UCHAR i = 0; i < streamCount; ++i)
|
||||
newStreamList[i] = remap[oldStreamList[i]];
|
||||
|
||||
node->nod_arg[e_derived_expr_stream_list] = (jrd_nod*) newStreamList;
|
||||
}
|
||||
else
|
||||
node->nod_arg[e_derived_expr_stream_list] = input->nod_arg[e_derived_expr_stream_list];
|
||||
|
||||
node->nod_arg[e_derived_expr_stream_count] = input->nod_arg[e_derived_expr_stream_count];
|
||||
return node;
|
||||
}
|
||||
|
||||
case nod_function:
|
||||
node = PAR_make_node(tdbb, e_fun_length);
|
||||
node->nod_count = input->nod_count;
|
||||
|
@ -916,6 +916,21 @@ dsc* EVL_expr(thread_db* tdbb, jrd_nod* const node)
|
||||
return &impure->vlu_desc;
|
||||
}
|
||||
|
||||
case nod_derived_expr:
|
||||
{
|
||||
UCHAR streamCount = (UCHAR)(IPTR) node->nod_arg[e_derived_expr_stream_count];
|
||||
USHORT* streamList = (USHORT*) node->nod_arg[e_derived_expr_stream_list];
|
||||
|
||||
for (UCHAR i = 0; i < streamCount; ++i)
|
||||
{
|
||||
if (request->req_rpb[streamList[i]].rpb_number.isValid())
|
||||
return EVL_expr(tdbb, node->nod_arg[e_derived_expr_expr]);
|
||||
}
|
||||
|
||||
request->req_flags |= req_null;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
case nod_function:
|
||||
FUN_evaluate(tdbb, reinterpret_cast<UserFunction*>(node->nod_arg[e_fun_function]),
|
||||
node->nod_arg[e_fun_args], impure);
|
||||
|
@ -561,6 +561,13 @@ const int e_stmt_expr_stmt = 0;
|
||||
const int e_stmt_expr_expr = 1;
|
||||
const int e_stmt_expr_length = 2;
|
||||
|
||||
// nod_derived_expr
|
||||
const int e_derived_expr_expr = 0;
|
||||
const int e_derived_expr_stream_count = 1;
|
||||
const int e_derived_expr_stream_list = 2;
|
||||
const int e_derived_expr_count = 1;
|
||||
const int e_derived_expr_length = 3;
|
||||
|
||||
// Request resources
|
||||
|
||||
struct Resource
|
||||
|
@ -278,7 +278,8 @@ const int op_relation = 20;
|
||||
const int op_exec_into = 21;
|
||||
const int op_cursor_stmt = 22;
|
||||
const int op_byte_opt_verb = 23;
|
||||
const int op_exec_stmt = 24;
|
||||
const int op_exec_stmt = 24;
|
||||
const int op_derived_expr = 25;
|
||||
|
||||
static const UCHAR
|
||||
/* generic print formats */
|
||||
@ -337,7 +338,8 @@ static const UCHAR
|
||||
trim[] = { op_byte, op_byte_opt_verb, op_verb, 0},
|
||||
modify2[] = { op_byte, op_byte, op_line, op_verb, op_verb, 0},
|
||||
similar[] = { op_line, op_verb, op_verb, op_indent, op_byte_opt_verb, 0},
|
||||
exec_stmt[] = { op_exec_stmt, 0};
|
||||
exec_stmt[] = { op_exec_stmt, 0},
|
||||
derived_expr[] = { op_derived_expr, 0};
|
||||
|
||||
|
||||
#include "../jrd/blp.h"
|
||||
@ -3394,6 +3396,14 @@ static void blr_print_verb(gds_ctl* control, SSHORT level)
|
||||
break;
|
||||
}
|
||||
|
||||
case op_derived_expr:
|
||||
n = blr_print_byte(control);
|
||||
for (UCHAR i = 0; i < (UCHAR) n; ++i)
|
||||
blr_print_byte(control);
|
||||
offset = blr_print_line(control, (SSHORT) offset);
|
||||
blr_print_verb(control, level);
|
||||
break;
|
||||
|
||||
case op_cursor_stmt: {
|
||||
blr_operator = blr_print_byte(control);
|
||||
blr_print_word(control);
|
||||
|
@ -200,4 +200,4 @@ NODE(nod_asn_list, asn_list, "")
|
||||
NODE(nod_sys_function, sys_function, "")
|
||||
NODE(nod_class_node_jrd, class_node_jrd, "class_node_jrd")
|
||||
NODE(nod_stmt_expr, stmt_expr, "stmt_expr")
|
||||
|
||||
NODE(nod_derived_expr, derived_expr, "derived_expr")
|
||||
|
@ -3096,6 +3096,20 @@ jrd_nod* PAR_parse_node(thread_db* tdbb, CompilerScratch* csb, USHORT expected,
|
||||
set_type = false; // to not change nod->nod_type to nod_field
|
||||
break;
|
||||
|
||||
case blr_derived_expr:
|
||||
{
|
||||
UCHAR streamCount = BLR_BYTE;
|
||||
USHORT* streamList = FB_NEW(*tdbb->getDefaultPool()) USHORT[streamCount];
|
||||
for (UCHAR i = 0; i < streamCount; ++i)
|
||||
streamList[i] = BLR_BYTE;
|
||||
|
||||
node->nod_arg[e_derived_expr_stream_list] = (jrd_nod*) streamList;
|
||||
node->nod_arg[e_derived_expr_stream_count] = (jrd_nod*)(IPTR) streamCount;
|
||||
node->nod_arg[e_derived_expr_expr] = PAR_parse_node(tdbb, csb, sub_type);
|
||||
node->nod_count = e_derived_expr_count;
|
||||
break;
|
||||
}
|
||||
|
||||
case blr_gen_id:
|
||||
case blr_set_generator:
|
||||
{
|
||||
|
@ -223,6 +223,7 @@ static const VERB verbs[] =
|
||||
PAIR(nod_class_node_jrd, blr_auto_trans, 1, 0, STATEMENT, STATEMENT),
|
||||
PAIR(nod_similar, blr_similar, 3, 3, TYPE_BOOL, VALUE),
|
||||
PAIR(nod_stmt_expr, blr_stmt_expr, e_stmt_expr_length, 2, VALUE, OTHER),
|
||||
PAIR(nod_derived_expr, blr_derived_expr, e_derived_expr_length, e_derived_expr_count, VALUE, VALUE),
|
||||
{0, NULL, NULL, NULL, NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user