8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-02-02 09:20:39 +01:00

Fixed bug CORE-6351 : Computed field could be wrongly evaluated as NULL

This commit is contained in:
hvlad 2020-08-05 21:39:05 +03:00
parent cc6d1f5595
commit 5f336a189e
11 changed files with 150 additions and 11 deletions

View File

@ -6541,7 +6541,12 @@ ValueExprNode* FieldNode::pass1(thread_db* tdbb, CompilerScratch* csb)
jrd_fld* field;
if (!relation || !(field = MET_get_field(relation, fieldId)))
{
if (relation && (relation->rel_flags & REL_being_scanned))
csb->csb_g_flags |= csb_reload;
return ValueExprNode::pass1(tdbb, csb);
}
dsc desc;
getDesc(tdbb, csb, &desc);
@ -6785,6 +6790,15 @@ dsc* FieldNode::execute(thread_db* tdbb, jrd_req* request) const
Record* record = rpb.rpb_record;
jrd_rel* relation = rpb.rpb_relation;
#ifdef DEV_BUILD
if (relation && !relation->isView())
{
// Computed fields shouldn't be present at this point
jrd_fld* field = MET_get_field(relation, fieldId);
fb_assert(field && !field->fld_computation);
}
#endif
// In order to "map a null to a default" value (in EVL_field()), the relation block is referenced.
// Reference: Bug 10116, 10424
@ -12944,6 +12958,8 @@ dsc* UdfCallNode::execute(thread_db* tdbb, jrd_req* request) const
}
else
{
const_cast<Function*>(function.getObject())->checkReload(tdbb);
Jrd::Attachment* attachment = tdbb->getAttachment();
const ULONG inMsgLength = function->getInputFormat() ? function->getInputFormat()->fmt_length : 0;

View File

@ -3218,6 +3218,8 @@ void ExecProcedureNode::executeProcedure(thread_db* tdbb, jrd_req* request) cons
Arg::Str(procedure->getName().identifier) << Arg::Str(procedure->getName().package));
}
const_cast<jrd_prc*>(procedure.getObject())->checkReload(tdbb);
UserId* invoker = procedure->invoker ? procedure->invoker : tdbb->getAttachment()->att_ss_user;
AutoSetRestore<UserId*> userIdHolder(&tdbb->getAttachment()->att_ss_user, invoker);

View File

@ -437,12 +437,10 @@ Function* Function::loadMetadata(thread_db* tdbb, USHORT id, bool noscan, USHORT
}
else if (!X.RDB$FUNCTION_BLR.NULL)
{
if (!X.RDB$DEBUG_INFO.NULL)
DBG_parse_debug_info(tdbb, &X.RDB$DEBUG_INFO, *csb->csb_dbg_info);
try
{
function->parseBlr(tdbb, csb, &X.RDB$FUNCTION_BLR);
function->parseBlr(tdbb, csb, &X.RDB$FUNCTION_BLR,
X.RDB$DEBUG_INFO.NULL ? NULL : &X.RDB$DEBUG_INFO);
}
catch (const Exception& ex)
{
@ -552,3 +550,47 @@ void Function::clearCache(thread_db* tdbb)
{
tdbb->getAttachment()->att_functions[getId()] = NULL;
}
bool Function::reload(thread_db* tdbb)
{
fb_assert(this->flags & Routine::FLAG_RELOAD);
Attachment* attachment = tdbb->getAttachment();
AutoCacheRequest request(tdbb, irq_l_funct_blr, IRQ_REQUESTS);
FOR(REQUEST_HANDLE request)
X IN RDB$FUNCTIONS
WITH X.RDB$FUNCTION_ID EQ this->getId()
{
if (X.RDB$FUNCTION_BLR.NULL)
continue;
MemoryPool* const csb_pool = attachment->createPool();
Jrd::ContextPoolHolder context(tdbb, csb_pool);
AutoPtr<CompilerScratch> csb(FB_NEW_POOL(*csb_pool) CompilerScratch(*csb_pool));
try
{
this->parseBlr(tdbb, csb, &X.RDB$FUNCTION_BLR,
X.RDB$DEBUG_INFO.NULL ? NULL : &X.RDB$DEBUG_INFO);
// parseBlr() above could set FLAG_RELOAD again
return !(this->flags & Routine::FLAG_RELOAD);
}
catch (const Exception& ex)
{
StaticStatusVector temp_status;
ex.stuffException(temp_status);
attachment->deletePool(csb_pool);
const string name = this->getName().toString();
(Arg::Gds(isc_bad_fun_BLR) << Arg::Str(name)
<< Arg::StatusVector(temp_status.begin())).raise();
}
}
END_FOR
return false;
}

View File

@ -91,6 +91,9 @@ namespace Jrd
bool fun_deterministic;
const ExtEngineManager::Function* fun_external;
protected:
virtual bool reload(thread_db* tdbb);
};
}

View File

@ -110,11 +110,30 @@ Format* Routine::createFormat(MemoryPool& pool, IMessageMetadata* params, bool a
return format;
}
// Parse routine BLR.
void Routine::parseBlr(thread_db* tdbb, CompilerScratch* csb, bid* blob_id)
void Routine::checkReload(thread_db* tdbb)
{
if (!(flags & FLAG_RELOAD))
return;
if (!reload(tdbb))
{
string err;
err.printf("Recompile of %s \"%s\" failed",
getObjectType() == obj_udf ? "FUNCTION" : "PROCEDURE",
getName().toString());
(Arg::Gds(isc_random) << Arg::Str(err)).raise();
}
}
// Parse routine BLR and debug info.
void Routine::parseBlr(thread_db* tdbb, CompilerScratch* csb, bid* blob_id, bid* blobDbg)
{
Jrd::Attachment* attachment = tdbb->getAttachment();
if (blobDbg)
DBG_parse_debug_info(tdbb, blobDbg, *csb->csb_dbg_info);
UCharBuffer tmp;
if (blob_id)
@ -128,10 +147,15 @@ void Routine::parseBlr(thread_db* tdbb, CompilerScratch* csb, bid* blob_id)
parseMessages(tdbb, csb, BlrReader(tmp.begin(), (unsigned) tmp.getCount()));
flags &= ~Routine::FLAG_RELOAD;
JrdStatement* statement = getStatement();
PAR_blr(tdbb, NULL, tmp.begin(), (ULONG) tmp.getCount(), NULL, &csb, &statement, false, 0);
setStatement(statement);
if (csb->csb_g_flags & csb_reload)
flags |= FLAG_RELOAD;
if (!blob_id)
setImplemented(false);
}

View File

@ -81,6 +81,7 @@ namespace Jrd
// so dfw.epp:modify_procedure() can flip procedure body without
// invalidating procedure pointers from other parts of metadata cache
static const USHORT FLAG_CHECK_EXISTENCE = 16; // Existence lock released
static const USHORT FLAG_RELOAD = 32; // Recompile before execution
static const USHORT MAX_ALTER_COUNT = 64; // Number of times an in-cache routine can be altered
@ -115,6 +116,8 @@ namespace Jrd
bool isDefined() const { return defined; }
void setDefined(bool value) { defined = value; }
void checkReload(thread_db* tdbb);
USHORT getDefaultCount() const { return defaultCount; }
void setDefaultCount(USHORT value) { defaultCount = value; }
@ -130,7 +133,7 @@ namespace Jrd
const Firebird::Array<NestConst<Parameter> >& getOutputFields() const { return outputFields; }
Firebird::Array<NestConst<Parameter> >& getOutputFields() { return outputFields; }
void parseBlr(thread_db* tdbb, CompilerScratch* csb, bid* blob_id);
void parseBlr(thread_db* tdbb, CompilerScratch* csb, bid* blob_id, bid* blobDbg);
void parseMessages(thread_db* tdbb, CompilerScratch* csb, Firebird::BlrReader blrReader);
bool isUsed() const
@ -172,6 +175,10 @@ namespace Jrd
Firebird::Array<NestConst<Parameter> > inputFields; // array of field blocks
Firebird::Array<NestConst<Parameter> > outputFields; // array of field blocks
protected:
virtual bool reload(thread_db* tdbb) = 0;
public:
USHORT flags;
USHORT useCount; // requests compiled with routine

View File

@ -616,6 +616,7 @@ const int csb_post_trigger = 32; // this is an AFTER trigger
const int csb_validation = 64; // we're in a validation expression (RDB hack)
const int csb_reuse_context = 128; // allow context reusage
const int csb_subroutine = 256; // sub routine
const int csb_reload = 512; // request's BLR should be loaded and parsed again
// CompilerScratch.csb_rpt[].csb_flags's values.
const int csb_active = 1; // stream is active

View File

@ -51,6 +51,7 @@ enum irq_type_t
irq_v_security_o, // verify security for role
irq_l_index, // lookup index id
irq_l_functions, // lookup function
irq_l_funct_blr, // lookup function BLR and debug info
irq_l_args, // lookup function arguments
irq_s_triggers, // scan triggers
irq_s_triggers2, // scan triggers
@ -90,6 +91,7 @@ enum irq_type_t
irq_r_params, // scan procedure parameters
irq_r_procedure, // scan procedure
irq_r_proc_blr, // look for procedure's BLR and debug info
irq_pkg_security, // verify security for package
irq_p_security, // verify security for procedure
irq_c_prc_dpd, // create procedure dependencies for delete

View File

@ -269,6 +269,9 @@ public:
delete prc_external;
prc_external = NULL;
}
protected:
virtual bool reload(thread_db* tdbb); // impl is in met.epp
};

View File

@ -3547,12 +3547,10 @@ jrd_prc* MET_procedure(thread_db* tdbb, USHORT id, bool noscan, USHORT flags)
}
else
{
if (!P.RDB$DEBUG_INFO.NULL)
DBG_parse_debug_info(tdbb, &P.RDB$DEBUG_INFO, *csb->csb_dbg_info);
try
{
procedure->parseBlr(tdbb, csb, &P.RDB$PROCEDURE_BLR);
procedure->parseBlr(tdbb, csb, &P.RDB$PROCEDURE_BLR,
P.RDB$DEBUG_INFO.NULL ? NULL : &P.RDB$DEBUG_INFO);
}
catch (const Exception& ex)
{
@ -3638,6 +3636,45 @@ jrd_prc* MET_procedure(thread_db* tdbb, USHORT id, bool noscan, USHORT flags)
return procedure;
}
bool jrd_prc::reload(thread_db* tdbb)
{
fb_assert(this->flags & Routine::FLAG_RELOAD);
Attachment* attachment = tdbb->getAttachment();
AutoCacheRequest request(tdbb, irq_r_proc_blr, IRQ_REQUESTS);
FOR(REQUEST_HANDLE request)
P IN RDB$PROCEDURES WITH P.RDB$PROCEDURE_ID EQ this->getId()
{
MemoryPool* const csb_pool = attachment->createPool();
Jrd::ContextPoolHolder context(tdbb, csb_pool);
AutoPtr<CompilerScratch> csb(FB_NEW_POOL(*csb_pool) CompilerScratch(*csb_pool));
try
{
this->parseBlr(tdbb, csb, &P.RDB$PROCEDURE_BLR,
P.RDB$DEBUG_INFO.NULL ? NULL : &P.RDB$DEBUG_INFO);
// parseBlr() above could set FLAG_RELOAD again
return !(this->flags & Routine::FLAG_RELOAD);
}
catch (const Exception& ex)
{
StaticStatusVector temp_status;
ex.stuffException(temp_status);
attachment->deletePool(csb_pool);
const string name = this->getName().toString();
(Arg::Gds(isc_bad_proc_BLR) << Arg::Str(name)
<< Arg::StatusVector(temp_status.begin())).raise();
}
}
END_FOR
return false;
}
jrd_rel* MET_relation(thread_db* tdbb, USHORT id)
{

View File

@ -62,6 +62,8 @@ void ProcedureScan::open(thread_db* tdbb) const
Arg::Str(m_procedure->getName().identifier) << Arg::Str(m_procedure->getName().package));
}
const_cast<jrd_prc*>(m_procedure)->checkReload(tdbb);
jrd_req* const request = tdbb->getRequest();
Impure* const impure = request->getImpure<Impure>(m_impure);