2010-09-17 05:15:32 +02:00
|
|
|
/*
|
|
|
|
* The contents of this file are subject to the Interbase Public
|
|
|
|
* License Version 1.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.Inprise.com/IPL.html
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an
|
|
|
|
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
|
|
|
|
* or implied. See the License for the specific language governing
|
|
|
|
* rights and limitations under the License.
|
|
|
|
*
|
|
|
|
* The Original Code was created by Inprise Corporation
|
|
|
|
* and its predecessors. Portions created by Inprise Corporation are
|
|
|
|
* Copyright (C) Inprise Corporation.
|
|
|
|
*
|
|
|
|
* All Rights Reserved.
|
|
|
|
* Contributor(s): ______________________________________.
|
|
|
|
* Adriano dos Santos Fernandes - refactored from pass1.cpp, gen.cpp, cmp.cpp, par.cpp and evl.cpp
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "firebird.h"
|
|
|
|
#include "../common/classes/VaryStr.h"
|
|
|
|
#include "../dsql/BoolNodes.h"
|
2010-10-24 02:26:00 +02:00
|
|
|
#include "../dsql/ExprNodes.h"
|
2012-09-24 16:26:33 +02:00
|
|
|
#include "../dsql/StmtNodes.h"
|
2010-09-17 05:15:32 +02:00
|
|
|
#include "../jrd/align.h"
|
2019-06-03 16:45:00 +02:00
|
|
|
#include "firebird/impl/blr.h"
|
2010-09-17 05:15:32 +02:00
|
|
|
#include "../jrd/tra.h"
|
|
|
|
#include "../jrd/recsrc/RecordSource.h"
|
2015-08-31 17:11:06 +02:00
|
|
|
#include "../jrd/recsrc/Cursor.h"
|
2022-02-09 07:35:31 +01:00
|
|
|
#include "../jrd/optimizer/Optimizer.h"
|
2010-09-17 05:15:32 +02:00
|
|
|
#include "../jrd/blb_proto.h"
|
2024-05-01 11:46:34 +02:00
|
|
|
#include "../jrd/btr_proto.h"
|
2010-09-17 05:15:32 +02:00
|
|
|
#include "../jrd/cmp_proto.h"
|
|
|
|
#include "../jrd/evl_proto.h"
|
|
|
|
#include "../jrd/intl_proto.h"
|
|
|
|
#include "../jrd/mov_proto.h"
|
|
|
|
#include "../jrd/par_proto.h"
|
2012-05-31 18:53:42 +02:00
|
|
|
#include "../jrd/Collation.h"
|
2010-09-17 05:15:32 +02:00
|
|
|
#include "../dsql/ddl_proto.h"
|
|
|
|
#include "../dsql/errd_proto.h"
|
|
|
|
#include "../dsql/gen_proto.h"
|
|
|
|
#include "../dsql/make_proto.h"
|
|
|
|
#include "../dsql/pass1_proto.h"
|
2023-09-04 08:13:10 +02:00
|
|
|
#include "../dsql/DSqlDataTypeUtil.h"
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
using namespace Firebird;
|
|
|
|
using namespace Jrd;
|
|
|
|
|
2024-02-02 11:07:28 +01:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
// Maximum members in "IN" list. For eg. SELECT * FROM T WHERE F IN (1, 2, 3, ...)
|
|
|
|
// Beware: raising the limit beyond the 16-bit boundaries would be an incompatible BLR change.
|
|
|
|
static const unsigned MAX_MEMBER_LIST = MAX_USHORT;
|
|
|
|
|
|
|
|
// Compare two comparisons with the boolean literal of the form:
|
|
|
|
// [NOT] <value> [{ = | <> } { TRUE | FALSE }]
|
|
|
|
// and detect whether they're logically the same.
|
|
|
|
// For example: (NOT A) == (A = FALSE) == (A <> TRUE) == NOT (A = TRUE) etc.
|
|
|
|
|
|
|
|
bool sameBoolComparison(const ComparativeBoolNode* node1, const ComparativeBoolNode* node2, bool ignoreStreams)
|
|
|
|
{
|
|
|
|
fb_assert(node1 && node2);
|
|
|
|
|
|
|
|
if (node1->blrOp != blr_eql && node1->blrOp != blr_neq)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (node2->blrOp != blr_eql && node2->blrOp != blr_neq)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
bool isTrue1 = false;
|
|
|
|
const ValueExprNode* arg1 = nullptr;
|
|
|
|
|
|
|
|
if (const auto literal = nodeAs<LiteralNode>(node1->arg1))
|
|
|
|
{
|
|
|
|
if (literal->litDesc.isBoolean())
|
|
|
|
{
|
|
|
|
isTrue1 = literal->getBoolean();
|
|
|
|
arg1 = node1->arg2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (const auto literal = nodeAs<LiteralNode>(node1->arg2))
|
|
|
|
{
|
|
|
|
if (literal->litDesc.isBoolean())
|
|
|
|
{
|
|
|
|
isTrue1 = literal->getBoolean();
|
|
|
|
arg1 = node1->arg1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!arg1)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (node1->blrOp == blr_neq)
|
|
|
|
isTrue1 = !isTrue1;
|
|
|
|
|
|
|
|
bool isTrue2 = false;
|
|
|
|
const ValueExprNode* arg2 = nullptr;
|
|
|
|
|
|
|
|
if (const auto literal = nodeAs<LiteralNode>(node2->arg1))
|
|
|
|
{
|
|
|
|
if (literal->litDesc.isBoolean())
|
|
|
|
{
|
|
|
|
isTrue2 = literal->getBoolean();
|
|
|
|
arg2 = node2->arg2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (const auto literal = nodeAs<LiteralNode>(node2->arg2))
|
|
|
|
{
|
|
|
|
if (literal->litDesc.isBoolean())
|
|
|
|
{
|
|
|
|
isTrue2 = literal->getBoolean();
|
|
|
|
arg2 = node2->arg1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!arg2)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (node2->blrOp == blr_neq)
|
|
|
|
isTrue2 = !isTrue2;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2024-02-02 11:07:28 +01:00
|
|
|
if (!arg1->sameAs(arg2, ignoreStreams) || isTrue1 != isTrue2)
|
|
|
|
return false;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2024-02-02 11:07:28 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
|
|
|
|
//--------------------
|
|
|
|
|
|
|
|
|
2010-09-20 18:07:50 +02:00
|
|
|
BoolExprNode* BoolExprNode::pass2(thread_db* tdbb, CompilerScratch* csb)
|
|
|
|
{
|
2023-04-27 01:02:39 +02:00
|
|
|
pass2Boolean(tdbb, csb, [=] { ExprNode::pass2(tdbb, csb); });
|
2010-09-20 18:07:50 +02:00
|
|
|
|
2010-11-14 23:31:42 +01:00
|
|
|
if (nodFlags & FLAG_INVARIANT)
|
2010-09-20 18:07:50 +02:00
|
|
|
{
|
|
|
|
// Bind values of invariant nodes to top-level RSE (if present)
|
|
|
|
|
|
|
|
if (csb->csb_current_nodes.hasData())
|
|
|
|
{
|
2017-06-09 19:09:36 +02:00
|
|
|
RseNode* topRseNode = nodeAs<RseNode>(csb->csb_current_nodes[0]);
|
2015-09-01 17:55:10 +02:00
|
|
|
fb_assert(topRseNode);
|
2010-09-20 18:07:50 +02:00
|
|
|
|
2015-09-01 17:55:10 +02:00
|
|
|
if (!topRseNode->rse_invariants)
|
2010-09-20 18:07:50 +02:00
|
|
|
{
|
2015-09-01 17:55:10 +02:00
|
|
|
topRseNode->rse_invariants =
|
2015-10-12 16:26:00 +02:00
|
|
|
FB_NEW_POOL(*tdbb->getDefaultPool()) VarInvariantArray(*tdbb->getDefaultPool());
|
2010-09-20 18:07:50 +02:00
|
|
|
}
|
|
|
|
|
2015-09-01 17:55:10 +02:00
|
|
|
topRseNode->rse_invariants->add(impureOffset);
|
2010-09-20 18:07:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//--------------------
|
|
|
|
|
|
|
|
|
2020-06-05 18:57:46 +02:00
|
|
|
static RegisterBoolNode<BinaryBoolNode> regBinaryBoolNode({blr_and, blr_or});
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
BinaryBoolNode::BinaryBoolNode(MemoryPool& pool, UCHAR aBlrOp, BoolExprNode* aArg1,
|
|
|
|
BoolExprNode* aArg2)
|
2010-09-17 05:15:32 +02:00
|
|
|
: TypedNode<BoolExprNode, ExprNode::TYPE_BINARY_BOOL>(pool),
|
|
|
|
blrOp(aBlrOp),
|
2012-05-03 18:43:29 +02:00
|
|
|
arg1(aArg1),
|
|
|
|
arg2(aArg2)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-04-09 04:28:38 +02:00
|
|
|
DmlNode* BinaryBoolNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2015-10-12 16:26:00 +02:00
|
|
|
BinaryBoolNode* node = FB_NEW_POOL(pool) BinaryBoolNode(pool, blrOp);
|
2010-09-20 18:07:50 +02:00
|
|
|
node->arg1 = PAR_parse_boolean(tdbb, csb);
|
|
|
|
node->arg2 = PAR_parse_boolean(tdbb, csb);
|
2010-09-17 05:15:32 +02:00
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2015-06-05 20:26:36 +02:00
|
|
|
string BinaryBoolNode::internalPrint(NodePrinter& printer) const
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2015-06-05 20:26:36 +02:00
|
|
|
BoolExprNode::internalPrint(printer);
|
|
|
|
|
|
|
|
NODE_PRINT(printer, blrOp);
|
|
|
|
NODE_PRINT(printer, arg1);
|
|
|
|
NODE_PRINT(printer, arg2);
|
|
|
|
|
|
|
|
return "BinaryBoolNode";
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
BoolExprNode* BinaryBoolNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
|
|
|
{
|
2017-09-27 17:55:08 +02:00
|
|
|
return FB_NEW_POOL(dsqlScratch->getPool()) BinaryBoolNode(dsqlScratch->getPool(), blrOp,
|
2012-05-03 18:43:29 +02:00
|
|
|
doDsqlPass(dsqlScratch, arg1), doDsqlPass(dsqlScratch, arg2));
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void BinaryBoolNode::genBlr(DsqlCompilerScratch* dsqlScratch)
|
|
|
|
{
|
|
|
|
dsqlScratch->appendUChar(blrOp);
|
2012-05-03 18:43:29 +02:00
|
|
|
GEN_expr(dsqlScratch, arg1);
|
|
|
|
GEN_expr(dsqlScratch, arg2);
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
2017-10-02 18:31:00 +02:00
|
|
|
bool BinaryBoolNode::dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2017-10-02 18:31:00 +02:00
|
|
|
if (!BoolExprNode::dsqlMatch(dsqlScratch, other, ignoreMapCast))
|
2010-09-17 05:15:32 +02:00
|
|
|
return false;
|
|
|
|
|
2017-06-09 19:09:36 +02:00
|
|
|
const BinaryBoolNode* o = nodeAs<BinaryBoolNode>(other);
|
2011-02-26 17:03:36 +01:00
|
|
|
fb_assert(o);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
return blrOp == o->blrOp;
|
|
|
|
}
|
|
|
|
|
2021-10-12 19:17:17 +02:00
|
|
|
bool BinaryBoolNode::sameAs(const ExprNode* other, bool ignoreStreams) const
|
2010-09-20 18:07:50 +02:00
|
|
|
{
|
2017-06-09 19:09:36 +02:00
|
|
|
const BinaryBoolNode* const otherNode = nodeAs<BinaryBoolNode>(other);
|
2010-09-20 18:07:50 +02:00
|
|
|
|
|
|
|
if (!otherNode || blrOp != otherNode->blrOp)
|
|
|
|
return false;
|
|
|
|
|
2021-10-12 19:17:17 +02:00
|
|
|
if (arg1->sameAs(otherNode->arg1, ignoreStreams) &&
|
|
|
|
arg2->sameAs(otherNode->arg2, ignoreStreams))
|
2010-09-20 18:07:50 +02:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// A AND B is equivalent to B AND A, ditto for A OR B and B OR A.
|
2021-10-12 19:17:17 +02:00
|
|
|
return arg1->sameAs(otherNode->arg2, ignoreStreams) &&
|
|
|
|
arg2->sameAs(otherNode->arg1, ignoreStreams);
|
2010-09-20 18:07:50 +02:00
|
|
|
}
|
|
|
|
|
2011-02-06 22:59:20 +01:00
|
|
|
BoolExprNode* BinaryBoolNode::copy(thread_db* tdbb, NodeCopier& copier) const
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2015-10-12 16:26:00 +02:00
|
|
|
BinaryBoolNode* node = FB_NEW_POOL(*tdbb->getDefaultPool()) BinaryBoolNode(*tdbb->getDefaultPool(),
|
2010-09-17 05:15:32 +02:00
|
|
|
blrOp);
|
2010-11-14 23:31:42 +01:00
|
|
|
node->nodFlags = nodFlags;
|
2010-11-21 04:47:29 +01:00
|
|
|
node->arg1 = copier.copy(tdbb, arg1);
|
|
|
|
node->arg2 = copier.copy(tdbb, arg2);
|
2010-09-17 05:15:32 +02:00
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2022-02-13 14:51:30 +01:00
|
|
|
bool BinaryBoolNode::execute(thread_db* tdbb, Request* request) const
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
|
|
|
switch (blrOp)
|
|
|
|
{
|
|
|
|
case blr_and:
|
|
|
|
return executeAnd(tdbb, request);
|
|
|
|
|
|
|
|
case blr_or:
|
|
|
|
return executeOr(tdbb, request);
|
|
|
|
}
|
|
|
|
|
|
|
|
fb_assert(false);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-02-13 14:51:30 +01:00
|
|
|
bool BinaryBoolNode::executeAnd(thread_db* tdbb, Request* request) const
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
|
|
|
// If either operand is false, then the result is false;
|
|
|
|
// If both are true, the result is true;
|
|
|
|
// Otherwise, the result is NULL.
|
|
|
|
//
|
|
|
|
// op 1 op 2 result
|
|
|
|
// ---- ---- ------
|
|
|
|
// F F F
|
|
|
|
// F T F
|
|
|
|
// F N F
|
|
|
|
// T F F
|
|
|
|
// T T T
|
|
|
|
// T N N
|
|
|
|
// N F F
|
|
|
|
// N T N
|
|
|
|
// N N N
|
|
|
|
|
2010-09-20 18:07:50 +02:00
|
|
|
const bool value1 = arg1->execute(tdbb, request);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
// Save null state and get other operand.
|
|
|
|
const USHORT firstnull = request->req_flags & req_null;
|
|
|
|
request->req_flags &= ~req_null;
|
|
|
|
|
|
|
|
if (!value1 && !firstnull)
|
|
|
|
{
|
|
|
|
// First term is false, why the whole expression is false.
|
|
|
|
// NULL flag is already turned off a few lines above.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-09-20 18:07:50 +02:00
|
|
|
const bool value2 = arg2->execute(tdbb, request);
|
2010-09-17 05:15:32 +02:00
|
|
|
const USHORT secondnull = request->req_flags & req_null;
|
|
|
|
request->req_flags &= ~req_null;
|
|
|
|
|
2010-09-25 22:26:05 +02:00
|
|
|
if (!value2 && !secondnull)
|
2010-09-17 05:15:32 +02:00
|
|
|
return false; // at least one operand was false
|
|
|
|
|
|
|
|
if (value1 && value2)
|
|
|
|
return true; // both true
|
|
|
|
|
|
|
|
// otherwise, return null
|
|
|
|
request->req_flags |= req_null;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-02-13 14:51:30 +01:00
|
|
|
bool BinaryBoolNode::executeOr(thread_db* tdbb, Request* request) const
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
|
|
|
// If either operand is true, then the result is true;
|
|
|
|
// If both are false, the result is false;
|
|
|
|
// Otherwise, the result is NULL.
|
|
|
|
//
|
|
|
|
// op 1 op 2 result
|
|
|
|
// ---- ---- ------
|
|
|
|
// F F F
|
|
|
|
// F T T
|
|
|
|
// F N N
|
|
|
|
// T F T
|
|
|
|
// T T T
|
|
|
|
// T N T
|
|
|
|
// N F N
|
|
|
|
// N T T
|
|
|
|
// N N N
|
|
|
|
|
2010-09-20 18:07:50 +02:00
|
|
|
const bool value1 = arg1->execute(tdbb, request);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
const ULONG flags = request->req_flags;
|
|
|
|
request->req_flags &= ~req_null;
|
|
|
|
|
|
|
|
if (value1)
|
|
|
|
{
|
|
|
|
// First term is true, why the whole expression is true.
|
|
|
|
// NULL flag is already turned off a few lines above.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2010-09-20 18:07:50 +02:00
|
|
|
const bool value2 = arg2->execute(tdbb, request);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
if (value1 || value2)
|
|
|
|
{
|
|
|
|
request->req_flags &= ~req_null;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// restore saved NULL state
|
|
|
|
|
|
|
|
if (flags & req_null)
|
|
|
|
request->req_flags |= req_null;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//--------------------
|
|
|
|
|
|
|
|
|
2020-06-05 18:57:46 +02:00
|
|
|
static RegisterBoolNode<ComparativeBoolNode> regComparativeBoolNode({
|
|
|
|
blr_eql,
|
|
|
|
blr_geq,
|
|
|
|
blr_gtr,
|
|
|
|
blr_leq,
|
|
|
|
blr_lss,
|
|
|
|
blr_neq,
|
|
|
|
blr_equiv,
|
|
|
|
blr_between,
|
|
|
|
blr_like,
|
|
|
|
blr_ansi_like,
|
|
|
|
blr_containing,
|
|
|
|
blr_starting,
|
|
|
|
blr_similar,
|
|
|
|
blr_matching,
|
|
|
|
blr_matching2
|
|
|
|
});
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
ComparativeBoolNode::ComparativeBoolNode(MemoryPool& pool, UCHAR aBlrOp,
|
2012-05-03 18:43:29 +02:00
|
|
|
ValueExprNode* aArg1, ValueExprNode* aArg2, ValueExprNode* aArg3)
|
2010-09-17 05:15:32 +02:00
|
|
|
: TypedNode<BoolExprNode, ExprNode::TYPE_COMPARATIVE_BOOL>(pool),
|
|
|
|
blrOp(aBlrOp),
|
2016-04-04 19:49:44 +02:00
|
|
|
dsqlCheckBoolean(false),
|
2010-09-17 05:15:32 +02:00
|
|
|
dsqlFlag(DFLAG_NONE),
|
2012-05-03 18:43:29 +02:00
|
|
|
arg1(aArg1),
|
|
|
|
arg2(aArg2),
|
|
|
|
arg3(aArg3),
|
2023-09-04 08:13:10 +02:00
|
|
|
dsqlSpecialArg(nullptr)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
ComparativeBoolNode::ComparativeBoolNode(MemoryPool& pool, UCHAR aBlrOp,
|
|
|
|
ValueExprNode* aArg1, DsqlFlag aDsqlFlag, ExprNode* aSpecialArg)
|
|
|
|
: TypedNode<BoolExprNode, ExprNode::TYPE_COMPARATIVE_BOOL>(pool),
|
|
|
|
blrOp(aBlrOp),
|
|
|
|
dsqlCheckBoolean(false),
|
|
|
|
dsqlFlag(aDsqlFlag),
|
|
|
|
arg1(aArg1),
|
|
|
|
arg2(nullptr),
|
|
|
|
arg3(nullptr),
|
|
|
|
dsqlSpecialArg(aSpecialArg)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-04-09 04:28:38 +02:00
|
|
|
DmlNode* ComparativeBoolNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2015-10-12 16:26:00 +02:00
|
|
|
ComparativeBoolNode* node = FB_NEW_POOL(pool) ComparativeBoolNode(pool, blrOp);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
node->arg1 = PAR_parse_value(tdbb, csb);
|
|
|
|
node->arg2 = PAR_parse_value(tdbb, csb);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
if (blrOp == blr_between || blrOp == blr_ansi_like || blrOp == blr_matching2)
|
|
|
|
{
|
|
|
|
if (blrOp == blr_ansi_like)
|
2010-09-18 20:18:35 +02:00
|
|
|
node->blrOp = blr_like;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
node->arg3 = PAR_parse_value(tdbb, csb);
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
else if (blrOp == blr_similar)
|
|
|
|
{
|
|
|
|
if (csb->csb_blr_reader.getByte() != 0)
|
2010-11-21 04:47:29 +01:00
|
|
|
node->arg3 = PAR_parse_value(tdbb, csb); // escape
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2015-06-05 20:26:36 +02:00
|
|
|
string ComparativeBoolNode::internalPrint(NodePrinter& printer) const
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2015-06-05 20:26:36 +02:00
|
|
|
BoolExprNode::internalPrint(printer);
|
|
|
|
|
|
|
|
NODE_PRINT(printer, blrOp);
|
|
|
|
NODE_PRINT(printer, dsqlFlag);
|
|
|
|
NODE_PRINT(printer, arg1);
|
|
|
|
NODE_PRINT(printer, arg2);
|
|
|
|
NODE_PRINT(printer, arg3);
|
|
|
|
NODE_PRINT(printer, dsqlSpecialArg);
|
|
|
|
|
|
|
|
return "ComparativeBoolNode";
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
BoolExprNode* ComparativeBoolNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
|
|
|
{
|
2012-05-03 18:43:29 +02:00
|
|
|
NestConst<ValueExprNode> procArg1 = arg1;
|
|
|
|
NestConst<ValueExprNode> procArg2 = arg2;
|
|
|
|
NestConst<ValueExprNode> procArg3 = arg3;
|
2010-12-18 03:17:06 +01:00
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
if (dsqlSpecialArg)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2023-09-04 08:13:10 +02:00
|
|
|
if (const auto listNode = nodeAs<ValueListNode>(dsqlSpecialArg))
|
2012-04-07 05:03:28 +02:00
|
|
|
{
|
2023-09-04 08:13:10 +02:00
|
|
|
if (listNode->items.getCount() > MAX_MEMBER_LIST)
|
|
|
|
{
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
|
|
|
|
Arg::Gds(isc_imp_exc) <<
|
|
|
|
Arg::Gds(isc_dsql_too_many_values) << Arg::Num(MAX_MEMBER_LIST));
|
|
|
|
}
|
2012-05-03 18:43:29 +02:00
|
|
|
|
2023-09-04 08:13:10 +02:00
|
|
|
if (listNode->items.getCount() == 1)
|
2012-04-07 05:03:28 +02:00
|
|
|
{
|
2023-09-04 08:13:10 +02:00
|
|
|
// Convert A IN (B) into A = B
|
|
|
|
|
|
|
|
ComparativeBoolNode* const resultNode = FB_NEW_POOL(dsqlScratch->getPool())
|
|
|
|
ComparativeBoolNode(dsqlScratch->getPool(),
|
|
|
|
blr_eql, arg1, listNode->items.front());
|
2012-05-03 18:43:29 +02:00
|
|
|
|
2023-09-04 08:13:10 +02:00
|
|
|
return resultNode->dsqlPass(dsqlScratch);
|
2012-04-07 05:03:28 +02:00
|
|
|
}
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2023-09-04 08:13:10 +02:00
|
|
|
// Generate the IN LIST boolean
|
|
|
|
|
|
|
|
InListBoolNode* const resultNode = FB_NEW_POOL(dsqlScratch->getPool())
|
|
|
|
InListBoolNode(dsqlScratch->getPool(), procArg1, listNode);
|
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
return resultNode->dsqlPass(dsqlScratch);
|
2012-04-07 05:03:28 +02:00
|
|
|
}
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2023-09-04 08:13:10 +02:00
|
|
|
if (const auto selNode = nodeAs<SelectExprNode>(dsqlSpecialArg))
|
2012-05-03 18:43:29 +02:00
|
|
|
{
|
|
|
|
fb_assert(!(selNode->dsqlFlags & RecordSourceNode::DFLAG_SINGLETON));
|
|
|
|
UCHAR newBlrOp = blr_any;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
if (dsqlFlag == DFLAG_ANSI_ANY)
|
|
|
|
newBlrOp = blr_ansi_any;
|
|
|
|
else if (dsqlFlag == DFLAG_ANSI_ALL)
|
|
|
|
newBlrOp = blr_ansi_all;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
return createRseNode(dsqlScratch, newBlrOp);
|
|
|
|
}
|
2012-04-07 05:03:28 +02:00
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
fb_assert(false);
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
procArg2 = doDsqlPass(dsqlScratch, procArg2);
|
2012-04-25 03:42:47 +02:00
|
|
|
|
2017-09-27 17:55:08 +02:00
|
|
|
ComparativeBoolNode* node = FB_NEW_POOL(dsqlScratch->getPool()) ComparativeBoolNode(dsqlScratch->getPool(), blrOp,
|
2012-04-25 03:42:47 +02:00
|
|
|
doDsqlPass(dsqlScratch, procArg1),
|
2012-05-03 18:43:29 +02:00
|
|
|
procArg2,
|
2012-04-25 03:42:47 +02:00
|
|
|
doDsqlPass(dsqlScratch, procArg3));
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2016-04-04 19:49:44 +02:00
|
|
|
if (dsqlCheckBoolean)
|
|
|
|
{
|
|
|
|
dsc desc;
|
2019-05-23 17:55:06 +02:00
|
|
|
DsqlDescMaker::fromNode(dsqlScratch, &desc, node->arg1);
|
2016-04-04 19:49:44 +02:00
|
|
|
|
2016-10-04 16:50:43 +02:00
|
|
|
if (desc.dsc_dtype != dtype_boolean && desc.dsc_dtype != dtype_unknown && !desc.isNull())
|
2016-04-04 19:49:44 +02:00
|
|
|
{
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_invalid_boolean_usage));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-17 05:15:32 +02:00
|
|
|
switch (blrOp)
|
|
|
|
{
|
|
|
|
case blr_eql:
|
|
|
|
case blr_neq:
|
|
|
|
case blr_gtr:
|
|
|
|
case blr_geq:
|
|
|
|
case blr_lss:
|
|
|
|
case blr_leq:
|
|
|
|
case blr_equiv:
|
|
|
|
case blr_between:
|
2010-12-18 03:17:06 +01:00
|
|
|
{
|
2010-09-17 05:15:32 +02:00
|
|
|
// Try to force arg1 to be same type as arg2 eg: ? = FIELD case
|
2012-05-03 18:43:29 +02:00
|
|
|
PASS1_set_parameter_type(dsqlScratch, node->arg1, procArg2, false);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
// Try to force arg2 to be same type as arg1 eg: FIELD = ? case
|
|
|
|
// Try even when the above call succeeded, because "arg2" may
|
|
|
|
// have arg-expressions that should be resolved.
|
2012-05-03 18:43:29 +02:00
|
|
|
PASS1_set_parameter_type(dsqlScratch, procArg2, node->arg1, false);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
// X BETWEEN Y AND ? case
|
2012-05-03 18:43:29 +02:00
|
|
|
if (!PASS1_set_parameter_type(dsqlScratch, node->arg3, node->arg1, false))
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
|
|
|
// ? BETWEEN Y AND ? case
|
2012-05-03 18:43:29 +02:00
|
|
|
PASS1_set_parameter_type(dsqlScratch, node->arg3, procArg2, false);
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
2010-12-18 03:17:06 +01:00
|
|
|
|
2010-09-17 05:15:32 +02:00
|
|
|
break;
|
2010-12-18 03:17:06 +01:00
|
|
|
}
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
case blr_containing:
|
|
|
|
case blr_like:
|
|
|
|
case blr_similar:
|
|
|
|
case blr_starting:
|
|
|
|
// Try to force arg1 to be same type as arg2 eg: ? LIKE FIELD case
|
2012-05-03 18:43:29 +02:00
|
|
|
PASS1_set_parameter_type(dsqlScratch, node->arg1, procArg2, true);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
// Try to force arg2 same type as arg 1 eg: FIELD LIKE ? case
|
|
|
|
// Try even when the above call succeeded, because "arg2" may
|
|
|
|
// have arg-expressions that should be resolved.
|
2012-05-03 18:43:29 +02:00
|
|
|
PASS1_set_parameter_type(dsqlScratch, procArg2, node->arg1, true);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
// X LIKE Y ESCAPE ? case
|
2012-05-03 18:43:29 +02:00
|
|
|
PASS1_set_parameter_type(dsqlScratch, node->arg3, procArg2, true);
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ComparativeBoolNode::genBlr(DsqlCompilerScratch* dsqlScratch)
|
|
|
|
{
|
2012-05-03 18:43:29 +02:00
|
|
|
dsqlScratch->appendUChar(blrOp == blr_like && arg3 ? blr_ansi_like : blrOp);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
GEN_expr(dsqlScratch, arg1);
|
|
|
|
GEN_expr(dsqlScratch, arg2);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
if (blrOp == blr_similar)
|
2012-05-03 18:43:29 +02:00
|
|
|
dsqlScratch->appendUChar(arg3 ? 1 : 0);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2012-05-03 18:43:29 +02:00
|
|
|
if (arg3)
|
|
|
|
GEN_expr(dsqlScratch, arg3);
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
2017-10-02 18:31:00 +02:00
|
|
|
bool ComparativeBoolNode::dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2017-10-02 18:31:00 +02:00
|
|
|
if (!BoolExprNode::dsqlMatch(dsqlScratch, other, ignoreMapCast))
|
2010-09-17 05:15:32 +02:00
|
|
|
return false;
|
|
|
|
|
2017-06-09 19:09:36 +02:00
|
|
|
const ComparativeBoolNode* o = nodeAs<ComparativeBoolNode>(other);
|
2011-02-26 17:03:36 +01:00
|
|
|
fb_assert(o);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
return dsqlFlag == o->dsqlFlag && blrOp == o->blrOp;
|
|
|
|
}
|
|
|
|
|
2021-10-12 19:17:17 +02:00
|
|
|
bool ComparativeBoolNode::sameAs(const ExprNode* other, bool ignoreStreams) const
|
2010-09-20 18:07:50 +02:00
|
|
|
{
|
2017-06-09 19:09:36 +02:00
|
|
|
const ComparativeBoolNode* const otherNode = nodeAs<ComparativeBoolNode>(other);
|
2010-09-20 18:07:50 +02:00
|
|
|
|
2024-02-02 11:07:28 +01:00
|
|
|
if (!otherNode)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (sameBoolComparison(this, otherNode, ignoreStreams))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (blrOp != otherNode->blrOp)
|
2010-09-20 18:07:50 +02:00
|
|
|
return false;
|
|
|
|
|
2021-10-12 19:17:17 +02:00
|
|
|
bool matching = arg1->sameAs(otherNode->arg1, ignoreStreams) &&
|
|
|
|
arg2->sameAs(otherNode->arg2, ignoreStreams);
|
2010-09-20 18:07:50 +02:00
|
|
|
|
|
|
|
if (matching)
|
|
|
|
{
|
2013-07-07 18:11:28 +02:00
|
|
|
matching = (!arg3 == !otherNode->arg3) &&
|
2021-10-12 19:17:17 +02:00
|
|
|
(!arg3 || arg3->sameAs(otherNode->arg3, ignoreStreams));
|
2010-09-20 18:07:50 +02:00
|
|
|
|
|
|
|
if (matching)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO match A > B to B <= A, etc
|
|
|
|
|
|
|
|
if (blrOp == blr_eql || blrOp == blr_equiv || blrOp == blr_neq)
|
|
|
|
{
|
|
|
|
// A = B is equivalent to B = A, etc.
|
2021-10-12 19:17:17 +02:00
|
|
|
if (arg1->sameAs(otherNode->arg2, ignoreStreams) &&
|
|
|
|
arg2->sameAs(otherNode->arg1, ignoreStreams))
|
2013-07-07 18:11:28 +02:00
|
|
|
{
|
2010-09-20 18:07:50 +02:00
|
|
|
return true;
|
2013-07-07 18:11:28 +02:00
|
|
|
}
|
2010-09-20 18:07:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-02-06 22:59:20 +01:00
|
|
|
BoolExprNode* ComparativeBoolNode::copy(thread_db* tdbb, NodeCopier& copier) const
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2015-10-12 16:26:00 +02:00
|
|
|
ComparativeBoolNode* node = FB_NEW_POOL(*tdbb->getDefaultPool()) ComparativeBoolNode(
|
2010-09-17 05:15:32 +02:00
|
|
|
*tdbb->getDefaultPool(), blrOp);
|
2010-11-14 23:31:42 +01:00
|
|
|
node->nodFlags = nodFlags;
|
2010-09-17 05:15:32 +02:00
|
|
|
node->arg1 = copier.copy(tdbb, arg1);
|
|
|
|
node->arg2 = copier.copy(tdbb, arg2);
|
|
|
|
|
|
|
|
if (arg3)
|
|
|
|
node->arg3 = copier.copy(tdbb, arg3);
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2010-09-20 18:07:50 +02:00
|
|
|
BoolExprNode* ComparativeBoolNode::pass1(thread_db* tdbb, CompilerScratch* csb)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
|
|
|
bool invariantCheck = false;
|
|
|
|
|
|
|
|
switch (blrOp)
|
|
|
|
{
|
|
|
|
case blr_like:
|
|
|
|
case blr_similar:
|
|
|
|
case blr_containing:
|
|
|
|
case blr_starting:
|
|
|
|
invariantCheck = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2010-12-04 23:15:03 +01:00
|
|
|
doPass1(tdbb, csb, arg1.getAddress());
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
if (invariantCheck)
|
|
|
|
{
|
|
|
|
// We need to take care of invariantness expressions to be able to pre-compile the pattern.
|
2010-11-14 23:31:42 +01:00
|
|
|
nodFlags |= FLAG_INVARIANT;
|
2010-09-20 18:07:50 +02:00
|
|
|
csb->csb_current_nodes.push(this);
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
2010-12-04 23:15:03 +01:00
|
|
|
doPass1(tdbb, csb, arg2.getAddress());
|
|
|
|
doPass1(tdbb, csb, arg3.getAddress());
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
if (invariantCheck)
|
|
|
|
{
|
|
|
|
csb->csb_current_nodes.pop();
|
|
|
|
|
|
|
|
// If there is no top-level RSE present and patterns are not constant, unmark node as invariant
|
|
|
|
// because it may be dependent on data or variables.
|
2010-11-14 23:31:42 +01:00
|
|
|
if ((nodFlags & FLAG_INVARIANT) &&
|
2017-06-09 19:09:36 +02:00
|
|
|
(!nodeIs<LiteralNode>(arg2) || (arg3 && !nodeIs<LiteralNode>(arg3))))
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2023-09-04 08:13:10 +02:00
|
|
|
for (const auto& ctxNode : csb->csb_current_nodes)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2023-09-04 08:13:10 +02:00
|
|
|
if (nodeIs<RseNode>(ctxNode))
|
|
|
|
return this;
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
2023-09-04 08:13:10 +02:00
|
|
|
nodFlags &= ~FLAG_INVARIANT;
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2023-04-27 01:02:39 +02:00
|
|
|
void ComparativeBoolNode::pass2Boolean(thread_db* tdbb, CompilerScratch* csb, std::function<void ()> process)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2010-11-14 23:31:42 +01:00
|
|
|
if (nodFlags & FLAG_INVARIANT)
|
2010-09-20 18:07:50 +02:00
|
|
|
csb->csb_invariants.push(&impureOffset);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2023-04-27 01:02:39 +02:00
|
|
|
process();
|
|
|
|
|
2010-11-14 18:25:48 +01:00
|
|
|
RecordKeyNode* keyNode;
|
|
|
|
|
2010-09-17 05:15:32 +02:00
|
|
|
if (arg3)
|
|
|
|
{
|
2017-06-09 19:09:36 +02:00
|
|
|
if ((keyNode = nodeAs<RecordKeyNode>(arg3)) && keyNode->aggregate)
|
2010-09-17 05:15:32 +02:00
|
|
|
ERR_post(Arg::Gds(isc_bad_dbkey));
|
|
|
|
|
|
|
|
dsc descriptor_c;
|
2010-11-21 04:47:29 +01:00
|
|
|
arg1->getDesc(tdbb, csb, &descriptor_c);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
if (DTYPE_IS_DATE(descriptor_c.dsc_dtype))
|
|
|
|
{
|
2010-11-21 04:47:29 +01:00
|
|
|
arg1->nodFlags |= FLAG_DATE;
|
|
|
|
arg2->nodFlags |= FLAG_DATE;
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-09 19:09:36 +02:00
|
|
|
if (((keyNode = nodeAs<RecordKeyNode>(arg1)) && keyNode->aggregate) ||
|
|
|
|
((keyNode = nodeAs<RecordKeyNode>(arg2)) && keyNode->aggregate))
|
2010-11-14 18:25:48 +01:00
|
|
|
{
|
2010-09-17 05:15:32 +02:00
|
|
|
ERR_post(Arg::Gds(isc_bad_dbkey));
|
2010-11-14 18:25:48 +01:00
|
|
|
}
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
dsc descriptor_a, descriptor_b;
|
2010-11-21 04:47:29 +01:00
|
|
|
arg1->getDesc(tdbb, csb, &descriptor_a);
|
|
|
|
arg2->getDesc(tdbb, csb, &descriptor_b);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
if (DTYPE_IS_DATE(descriptor_a.dsc_dtype))
|
2010-11-21 04:47:29 +01:00
|
|
|
arg2->nodFlags |= FLAG_DATE;
|
2010-09-17 05:15:32 +02:00
|
|
|
else if (DTYPE_IS_DATE(descriptor_b.dsc_dtype))
|
2010-11-21 04:47:29 +01:00
|
|
|
arg1->nodFlags |= FLAG_DATE;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2010-11-14 23:31:42 +01:00
|
|
|
if (nodFlags & FLAG_INVARIANT)
|
2021-07-02 16:19:30 +02:00
|
|
|
impureOffset = csb->allocImpure<impure_value>();
|
|
|
|
// Do not use FLAG_PATTERN_MATCHER_CACHE for blr_starting as it has very fast compilation.
|
|
|
|
else if (blrOp == blr_containing || blrOp == blr_like || blrOp == blr_similar)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2020-06-11 17:55:00 +02:00
|
|
|
impureOffset = csb->allocImpure<impure_value>();
|
2021-07-02 16:19:30 +02:00
|
|
|
nodFlags |= FLAG_PATTERN_MATCHER_CACHE;
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-13 14:51:30 +01:00
|
|
|
bool ComparativeBoolNode::execute(thread_db* tdbb, Request* request) const
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
|
|
|
dsc* desc[2] = {NULL, NULL};
|
|
|
|
bool computed_invariant = false;
|
|
|
|
|
|
|
|
request->req_flags &= ~req_same_tx_upd;
|
|
|
|
|
|
|
|
// Evaluate arguments. If either is null, result is null, but in
|
|
|
|
// any case, evaluate both, since some expressions may later depend
|
|
|
|
// on mappings which are developed here
|
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
desc[0] = EVL_expr(tdbb, request, arg1);
|
|
|
|
|
2023-11-13 09:16:28 +01:00
|
|
|
// arg1 IS NULL
|
|
|
|
const bool null1 = (request->req_flags & req_null);
|
|
|
|
|
2010-09-17 05:15:32 +02:00
|
|
|
request->req_flags &= ~req_null;
|
2014-02-25 07:20:09 +01:00
|
|
|
bool force_equal = (request->req_flags & req_same_tx_upd) != 0;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
// Currently only nod_like, nod_contains, nod_starts and nod_similar may be marked invariant
|
2010-11-14 23:31:42 +01:00
|
|
|
if (nodFlags & FLAG_INVARIANT)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2010-09-20 18:07:50 +02:00
|
|
|
impure_value* impure = request->getImpure<impure_value>(impureOffset);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
// Check that data type of operand is still the same.
|
|
|
|
// It may change due to multiple formats present in stream
|
|
|
|
// System tables are the good example of such streams -
|
|
|
|
// data coming from ini.epp has ASCII ttype, user data is UNICODE_FSS
|
|
|
|
//
|
|
|
|
// Note that value descriptor may be NULL pointer if value is SQL NULL
|
|
|
|
if ((impure->vlu_flags & VLU_computed) && desc[0] &&
|
|
|
|
(impure->vlu_desc.dsc_dtype != desc[0]->dsc_dtype ||
|
|
|
|
impure->vlu_desc.dsc_sub_type != desc[0]->dsc_sub_type ||
|
|
|
|
impure->vlu_desc.dsc_scale != desc[0]->dsc_scale))
|
|
|
|
{
|
|
|
|
impure->vlu_flags &= ~VLU_computed;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (impure->vlu_flags & VLU_computed)
|
|
|
|
{
|
|
|
|
if (impure->vlu_flags & VLU_null)
|
|
|
|
request->req_flags |= req_null;
|
|
|
|
else
|
|
|
|
computed_invariant = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-11-21 04:47:29 +01:00
|
|
|
desc[1] = EVL_expr(tdbb, request, arg2);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
if (request->req_flags & req_null)
|
|
|
|
{
|
|
|
|
impure->vlu_flags |= VLU_computed;
|
|
|
|
impure->vlu_flags |= VLU_null;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
impure->vlu_flags &= ~VLU_null;
|
|
|
|
|
|
|
|
// Search object depends on operand data type.
|
|
|
|
// Thus save data type which we use to compute invariant
|
|
|
|
if (desc[0])
|
|
|
|
{
|
|
|
|
impure->vlu_desc.dsc_dtype = desc[0]->dsc_dtype;
|
|
|
|
impure->vlu_desc.dsc_sub_type = desc[0]->dsc_sub_type;
|
|
|
|
impure->vlu_desc.dsc_scale = desc[0]->dsc_scale;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Indicate we do not know type of expression.
|
|
|
|
// This code will force pattern recompile for the next non-null value
|
|
|
|
impure->vlu_desc.dsc_dtype = 0;
|
|
|
|
impure->vlu_desc.dsc_sub_type = 0;
|
|
|
|
impure->vlu_desc.dsc_scale = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2010-11-21 04:47:29 +01:00
|
|
|
desc[1] = EVL_expr(tdbb, request, arg2);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2023-11-13 09:16:28 +01:00
|
|
|
// arg2 IS NULL
|
|
|
|
const bool null2 = (request->req_flags & req_null);
|
|
|
|
|
2010-09-17 05:15:32 +02:00
|
|
|
// An equivalence operator evaluates to true when both operands
|
|
|
|
// are NULL and behaves like an equality operator otherwise.
|
|
|
|
// Note that this operator never sets req_null flag
|
|
|
|
|
|
|
|
if (blrOp == blr_equiv)
|
|
|
|
{
|
2023-11-13 09:16:28 +01:00
|
|
|
if (null1 && null2)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
|
|
|
request->req_flags &= ~req_null;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-11-13 09:16:28 +01:00
|
|
|
if (null1 || null2)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
|
|
|
request->req_flags &= ~req_null;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If either of expressions above returned NULL set req_null flag
|
2023-11-13 09:16:28 +01:00
|
|
|
// and return false. The exception is BETWEEN operator that could
|
|
|
|
// return FALSE even when arg2 IS NULL, for example:
|
|
|
|
// 1 BETWEEN NULL AND 0
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2023-11-13 09:16:28 +01:00
|
|
|
if (null1 || (null2 && (blrOp != blr_between)))
|
|
|
|
{
|
2010-09-17 05:15:32 +02:00
|
|
|
request->req_flags |= req_null;
|
|
|
|
return false;
|
2023-11-13 09:16:28 +01:00
|
|
|
}
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2014-02-25 07:20:09 +01:00
|
|
|
force_equal |= (request->req_flags & req_same_tx_upd) != 0;
|
|
|
|
int comparison; // while the two switch() below are in sync, no need to initialize
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2010-09-20 18:07:50 +02:00
|
|
|
switch (blrOp)
|
|
|
|
{
|
|
|
|
case blr_eql:
|
|
|
|
case blr_equiv:
|
|
|
|
case blr_gtr:
|
|
|
|
case blr_geq:
|
|
|
|
case blr_lss:
|
|
|
|
case blr_leq:
|
|
|
|
case blr_neq:
|
2016-11-11 15:59:55 +01:00
|
|
|
comparison = MOV_compare(tdbb, desc[0], desc[1]);
|
2023-11-13 09:16:28 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case blr_between:
|
|
|
|
if (!null2)
|
|
|
|
{
|
|
|
|
comparison = MOV_compare(tdbb, desc[0], desc[1]);
|
|
|
|
if (comparison < 0)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
comparison = -1;
|
2023-11-16 12:36:17 +01:00
|
|
|
break;
|
2010-09-20 18:07:50 +02:00
|
|
|
}
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
// If we are checking equality of record_version
|
|
|
|
// and same transaction updated the record, force equality.
|
|
|
|
|
2017-06-09 19:09:36 +02:00
|
|
|
const RecordKeyNode* recVersionNode = nodeAs<RecordKeyNode>(arg1);
|
2010-11-14 18:25:48 +01:00
|
|
|
|
|
|
|
if (recVersionNode && recVersionNode->blrOp == blr_record_version && force_equal)
|
2010-09-17 05:15:32 +02:00
|
|
|
comparison = 0;
|
|
|
|
|
|
|
|
request->req_flags &= ~(req_null | req_same_tx_upd);
|
|
|
|
|
|
|
|
switch (blrOp)
|
|
|
|
{
|
|
|
|
case blr_eql:
|
|
|
|
case blr_equiv:
|
|
|
|
return comparison == 0;
|
|
|
|
|
|
|
|
case blr_gtr:
|
|
|
|
return comparison > 0;
|
|
|
|
|
|
|
|
case blr_geq:
|
|
|
|
return comparison >= 0;
|
|
|
|
|
|
|
|
case blr_lss:
|
|
|
|
return comparison < 0;
|
|
|
|
|
|
|
|
case blr_leq:
|
|
|
|
return comparison <= 0;
|
|
|
|
|
|
|
|
case blr_neq:
|
|
|
|
return comparison != 0;
|
|
|
|
|
|
|
|
case blr_between:
|
2010-11-21 04:47:29 +01:00
|
|
|
desc[1] = EVL_expr(tdbb, request, arg3);
|
2010-09-17 05:15:32 +02:00
|
|
|
if (request->req_flags & req_null)
|
2023-11-13 09:16:28 +01:00
|
|
|
{
|
|
|
|
if (!null2 && comparison < 0)
|
|
|
|
request->req_flags &= ~req_null;
|
2010-09-17 05:15:32 +02:00
|
|
|
return false;
|
2023-11-13 09:16:28 +01:00
|
|
|
}
|
|
|
|
{
|
|
|
|
// arg1 <= arg3
|
|
|
|
const bool cmp1_3 = (MOV_compare(tdbb, desc[0], desc[1]) <= 0);
|
|
|
|
if (null2)
|
|
|
|
{
|
|
|
|
if (cmp1_3)
|
|
|
|
request->req_flags |= req_null;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return cmp1_3;
|
|
|
|
}
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
case blr_containing:
|
|
|
|
case blr_starting:
|
|
|
|
case blr_matching:
|
|
|
|
case blr_like:
|
|
|
|
case blr_similar:
|
|
|
|
return stringBoolean(tdbb, request, desc[0], desc[1], computed_invariant);
|
|
|
|
|
|
|
|
case blr_matching2:
|
|
|
|
return sleuth(tdbb, request, desc[0], desc[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Perform one of the complex string functions CONTAINING, MATCHES, or STARTS WITH.
|
2022-02-13 14:51:30 +01:00
|
|
|
bool ComparativeBoolNode::stringBoolean(thread_db* tdbb, Request* request, dsc* desc1,
|
2021-07-02 16:19:30 +02:00
|
|
|
dsc* desc2, bool computedInvariant) const
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
USHORT type1;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
if (!desc1->isBlob())
|
2010-09-17 05:15:32 +02:00
|
|
|
type1 = INTL_TEXT_TYPE(*desc1);
|
|
|
|
else
|
|
|
|
{
|
2021-07-02 16:19:30 +02:00
|
|
|
// No MATCHES support for blob
|
|
|
|
if (blrOp == blr_matching)
|
|
|
|
return false;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
type1 = desc1->dsc_sub_type == isc_blob_text ? desc1->dsc_blob_ttype() : ttype_none;
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
Collation* obj = INTL_texttype_lookup(tdbb, type1);
|
|
|
|
CharSet* charset = obj->getCharSet();
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
VaryStr<TEMP_STR_LENGTH> escapeTemp;
|
|
|
|
const UCHAR* escapeStr = nullptr;
|
|
|
|
USHORT escapeLen = 0;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
// Handle escape for LIKE and SIMILAR
|
|
|
|
if (blrOp == blr_like || blrOp == blr_similar)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2021-07-02 16:19:30 +02:00
|
|
|
// ensure 3rd argument (escape char) is in operation text type
|
|
|
|
if (arg3 && !computedInvariant)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2021-07-02 16:19:30 +02:00
|
|
|
// Convert ESCAPE to operation character set
|
|
|
|
dsc* desc = EVL_expr(tdbb, request, arg3);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
if (request->req_flags & req_null)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2021-07-02 16:19:30 +02:00
|
|
|
if (nodFlags & FLAG_INVARIANT)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2021-07-02 16:19:30 +02:00
|
|
|
impure_value* impure = request->getImpure<impure_value>(impureOffset);
|
|
|
|
impure->vlu_flags |= VLU_computed;
|
|
|
|
impure->vlu_flags |= VLU_null;
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
2021-07-02 16:19:30 +02:00
|
|
|
return false;
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
escapeLen = MOV_make_string(tdbb, desc, type1,
|
|
|
|
reinterpret_cast<const char**>(&escapeStr), &escapeTemp, sizeof(escapeTemp));
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
if (!escapeLen || charset->length(escapeLen, escapeStr, true) != 1)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2021-07-02 16:19:30 +02:00
|
|
|
// If characters left, or null byte character, return error
|
|
|
|
ERR_post(Arg::Gds(isc_escape_invalid));
|
|
|
|
}
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
USHORT escape[2] = {0, 0};
|
2021-06-14 19:51:20 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
charset->getConvToUnicode().convert(escapeLen, escapeStr, sizeof(escape), escape);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
if (!escape[0])
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2021-07-02 16:19:30 +02:00
|
|
|
// If or null byte character, return error
|
|
|
|
ERR_post(Arg::Gds(isc_escape_invalid));
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
}
|
2021-07-02 16:19:30 +02:00
|
|
|
}
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
UCHAR* patternStr = nullptr;
|
|
|
|
SLONG patternLen = 0;
|
|
|
|
MoveBuffer patternBuffer;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
auto createMatcher = [&]()
|
|
|
|
{
|
|
|
|
return blrOp == blr_containing ? obj->createContainsMatcher(*tdbb->getDefaultPool(), patternStr, patternLen) :
|
|
|
|
blrOp == blr_starting ? obj->createStartsMatcher(*tdbb->getDefaultPool(), patternStr, patternLen) :
|
|
|
|
blrOp == blr_like ? obj->createLikeMatcher(*tdbb->getDefaultPool(),
|
|
|
|
patternStr, patternLen, escapeStr, escapeLen) :
|
|
|
|
blrOp == blr_similar ? obj->createSimilarToMatcher(tdbb, *tdbb->getDefaultPool(),
|
|
|
|
patternStr, patternLen, escapeStr, escapeLen) :
|
|
|
|
nullptr; // blr_matching
|
|
|
|
};
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
// Get address and length of search string - convert to datatype of data
|
|
|
|
if (!computedInvariant)
|
|
|
|
patternLen = MOV_make_string2(tdbb, desc2, type1, &patternStr, patternBuffer, false);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
AutoPtr<PatternMatcher> autoEvaluator; // deallocate non-invariant/non-cached evaluator
|
|
|
|
PatternMatcher* evaluator;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
impure_value* impure = request->getImpure<impure_value>(impureOffset);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
if (nodFlags & FLAG_INVARIANT)
|
|
|
|
{
|
|
|
|
auto& matcher = impure->vlu_misc.vlu_invariant;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
if (!(impure->vlu_flags & VLU_computed))
|
|
|
|
{
|
|
|
|
delete matcher;
|
|
|
|
matcher = nullptr;
|
|
|
|
matcher = createMatcher();
|
|
|
|
impure->vlu_flags |= VLU_computed;
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
2021-07-02 16:19:30 +02:00
|
|
|
else
|
|
|
|
matcher->reset();
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
evaluator = matcher;
|
|
|
|
}
|
|
|
|
else if (nodFlags & FLAG_PATTERN_MATCHER_CACHE)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2021-07-02 16:19:30 +02:00
|
|
|
auto& cache = impure->vlu_misc.vlu_patternMatcherCache;
|
|
|
|
const bool cacheHit = cache &&
|
|
|
|
cache->matcher &&
|
|
|
|
cache->ttype == type1 &&
|
|
|
|
cache->patternLen == patternLen &&
|
|
|
|
cache->escapeLen == escapeLen &&
|
|
|
|
memcmp(cache->key, patternStr, patternLen) == 0 &&
|
|
|
|
memcmp(cache->key + patternLen, escapeStr, escapeLen) == 0;
|
|
|
|
|
|
|
|
if (cacheHit)
|
|
|
|
cache->matcher->reset();
|
|
|
|
else
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2021-07-02 16:19:30 +02:00
|
|
|
if (cache && cache->keySize < patternLen + escapeLen)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2021-07-02 16:19:30 +02:00
|
|
|
delete cache;
|
|
|
|
cache = nullptr;
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
2021-07-02 16:19:30 +02:00
|
|
|
|
|
|
|
if (!cache)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2021-07-02 16:19:30 +02:00
|
|
|
cache = FB_NEW_RPT(*tdbb->getDefaultPool(), patternLen + escapeLen)
|
|
|
|
impure_value::PatternMatcherCache(patternLen + escapeLen);
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
cache->ttype = type1;
|
|
|
|
cache->patternLen = patternLen;
|
|
|
|
cache->escapeLen = escapeLen;
|
|
|
|
memcpy(cache->key, patternStr, patternLen);
|
|
|
|
memcpy(cache->key + patternLen, escapeStr, escapeLen);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
cache->matcher = createMatcher();
|
|
|
|
}
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
evaluator = cache->matcher;
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
2021-07-02 16:19:30 +02:00
|
|
|
else
|
|
|
|
autoEvaluator = evaluator = desc1->isBlob() ? createMatcher() : nullptr;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
if (!desc1->isBlob())
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2021-07-02 16:19:30 +02:00
|
|
|
// Source is not a blob, do a simple search
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
VaryStr<256> temp1;
|
|
|
|
UCHAR* str = NULL;
|
|
|
|
const USHORT strLen = MOV_get_string_ptr(tdbb, desc1, &type1, &str, &temp1, sizeof(temp1));
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
if (evaluator)
|
|
|
|
{
|
|
|
|
evaluator->process(str, strLen);
|
|
|
|
return evaluator->result();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (blrOp == blr_containing)
|
|
|
|
return obj->contains(*tdbb->getDefaultPool(), str, strLen, patternStr, patternLen);
|
|
|
|
else if (blrOp == blr_starting)
|
|
|
|
return obj->starts(*tdbb->getDefaultPool(), str, strLen, patternStr, patternLen);
|
|
|
|
else if (blrOp == blr_like)
|
|
|
|
return obj->like(*tdbb->getDefaultPool(), str, strLen, patternStr, patternLen, escapeStr, escapeLen);
|
|
|
|
else if (blrOp == blr_similar)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2021-07-02 16:19:30 +02:00
|
|
|
return obj->similarTo(tdbb, *tdbb->getDefaultPool(),
|
|
|
|
str, strLen, patternStr, patternLen, escapeStr, escapeLen);
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
2021-07-02 16:19:30 +02:00
|
|
|
else // blr_matching
|
|
|
|
return obj->matches(*tdbb->getDefaultPool(), str, strLen, patternStr, patternLen);
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
2021-07-02 16:19:30 +02:00
|
|
|
}
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
fb_assert(evaluator);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
// Source string is a blob, things get interesting
|
2021-06-14 19:51:20 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
AutoBlb blob(tdbb, blb::open(tdbb, request->req_transaction, reinterpret_cast<bid*>(desc1->dsc_address)));
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
HalfStaticArray<UCHAR, BUFFER_SMALL> buffer;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
if (charset->isMultiByte() &&
|
|
|
|
(blrOp != blr_starting || !(obj->getFlags() & TEXTTYPE_DIRECT_MATCH)))
|
|
|
|
{
|
|
|
|
buffer.getBuffer(blob->blb_length); // alloc space to put entire blob in memory
|
|
|
|
}
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
// Performs the string_function on each segment of the blob until
|
|
|
|
// a positive result is obtained
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
while (!(blob->blb_flags & BLB_eof))
|
|
|
|
{
|
|
|
|
const SLONG bufferLen = blob->BLB_get_data(tdbb, buffer.begin(), buffer.getCapacity(), false);
|
|
|
|
if (!evaluator->process(buffer.begin(), bufferLen))
|
|
|
|
break;
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
2021-07-02 16:19:30 +02:00
|
|
|
return evaluator->result();
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Execute SLEUTH operator.
|
2022-02-13 14:51:30 +01:00
|
|
|
bool ComparativeBoolNode::sleuth(thread_db* tdbb, Request* request, const dsc* desc1,
|
2010-09-17 05:15:32 +02:00
|
|
|
const dsc* desc2) const
|
|
|
|
{
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
|
|
|
// Choose interpretation for the operation
|
|
|
|
|
|
|
|
USHORT ttype;
|
|
|
|
if (desc1->isBlob())
|
|
|
|
{
|
|
|
|
if (desc1->dsc_sub_type == isc_blob_text)
|
|
|
|
ttype = desc1->dsc_blob_ttype(); // Load blob character set and collation
|
|
|
|
else
|
|
|
|
ttype = INTL_TTYPE(desc2);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ttype = INTL_TTYPE(desc1);
|
|
|
|
|
|
|
|
Collation* obj = INTL_texttype_lookup(tdbb, ttype);
|
|
|
|
|
|
|
|
// Get operator definition string (control string)
|
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
dsc* desc3 = EVL_expr(tdbb, request, arg3);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
UCHAR* p1;
|
|
|
|
MoveBuffer sleuth_str;
|
|
|
|
USHORT l1 = MOV_make_string2(tdbb, desc3, ttype, &p1, sleuth_str);
|
|
|
|
// Get address and length of search string
|
|
|
|
UCHAR* p2;
|
|
|
|
MoveBuffer match_str;
|
|
|
|
USHORT l2 = MOV_make_string2(tdbb, desc2, ttype, &p2, match_str);
|
|
|
|
|
|
|
|
// Merge search and control strings
|
|
|
|
UCHAR control[BUFFER_SMALL];
|
2014-03-03 05:37:29 +01:00
|
|
|
const SLONG control_length = obj->sleuthMerge(*tdbb->getDefaultPool(), p2, l2, p1, l1, control); //, BUFFER_SMALL);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
// Note: resulting string from sleuthMerge is either USHORT or UCHAR
|
|
|
|
// and never Multibyte (see note in EVL_mb_sleuthCheck)
|
|
|
|
bool ret_val;
|
|
|
|
MoveBuffer data_str;
|
|
|
|
if (!desc1->isBlob())
|
|
|
|
{
|
|
|
|
// Source is not a blob, do a simple search
|
|
|
|
|
|
|
|
l1 = MOV_make_string2(tdbb, desc1, ttype, &p1, data_str);
|
|
|
|
ret_val = obj->sleuthCheck(*tdbb->getDefaultPool(), 0, p1, l1, control, control_length);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Source string is a blob, things get interesting
|
|
|
|
|
2012-02-15 04:34:21 +01:00
|
|
|
blb* blob = blb::open(tdbb, request->req_transaction,
|
2010-09-17 05:15:32 +02:00
|
|
|
reinterpret_cast<bid*>(desc1->dsc_address));
|
|
|
|
|
|
|
|
UCHAR buffer[BUFFER_LARGE];
|
|
|
|
ret_val = false;
|
|
|
|
|
|
|
|
while (!(blob->blb_flags & BLB_eof))
|
|
|
|
{
|
2012-02-07 04:17:52 +01:00
|
|
|
l1 = blob->BLB_get_segment(tdbb, buffer, sizeof(buffer));
|
2010-09-17 05:15:32 +02:00
|
|
|
if (obj->sleuthCheck(*tdbb->getDefaultPool(), 0, buffer, l1, control, control_length))
|
|
|
|
{
|
|
|
|
ret_val = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-07 04:17:52 +01:00
|
|
|
blob->BLB_close(tdbb);
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret_val;
|
|
|
|
}
|
|
|
|
|
|
|
|
BoolExprNode* ComparativeBoolNode::createRseNode(DsqlCompilerScratch* dsqlScratch, UCHAR rseBlrOp)
|
|
|
|
{
|
2017-09-27 17:55:08 +02:00
|
|
|
MemoryPool& pool = dsqlScratch->getPool();
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
// Create a derived table representing our subquery.
|
2017-09-27 17:55:08 +02:00
|
|
|
SelectExprNode* dt = FB_NEW_POOL(pool) SelectExprNode(pool);
|
2011-04-02 06:24:20 +02:00
|
|
|
// Ignore validation for column names that must exist for "user" derived tables.
|
2012-04-07 05:03:28 +02:00
|
|
|
dt->dsqlFlags = RecordSourceNode::DFLAG_DT_IGNORE_COLUMN_CHECK | RecordSourceNode::DFLAG_DERIVED;
|
2012-05-03 18:43:29 +02:00
|
|
|
dt->querySpec = static_cast<RecordSourceNode*>(dsqlSpecialArg.getObject());
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2017-09-27 17:55:08 +02:00
|
|
|
RseNode* querySpec = FB_NEW_POOL(pool) RseNode(pool);
|
|
|
|
querySpec->dsqlFrom = FB_NEW_POOL(pool) RecSourceListNode(pool, 1);
|
2012-05-03 18:43:29 +02:00
|
|
|
querySpec->dsqlFrom->items[0] = dt;
|
2011-01-09 22:58:56 +01:00
|
|
|
|
2017-09-27 17:55:08 +02:00
|
|
|
SelectExprNode* select_expr = FB_NEW_POOL(pool) SelectExprNode(pool);
|
2012-04-25 03:42:47 +02:00
|
|
|
select_expr->querySpec = querySpec;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
const DsqlContextStack::iterator base(*dsqlScratch->context);
|
|
|
|
const DsqlContextStack::iterator baseDT(dsqlScratch->derivedContext);
|
|
|
|
const DsqlContextStack::iterator baseUnion(dsqlScratch->unionContext);
|
|
|
|
|
2023-09-13 20:21:12 +02:00
|
|
|
RseNode* rse = PASS1_rse(dsqlScratch, select_expr);
|
2012-09-24 03:40:44 +02:00
|
|
|
rse->flags |= RseNode::FLAG_DSQL_COMPARATIVE;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
// Create a conjunct to be injected.
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2017-09-27 17:55:08 +02:00
|
|
|
ComparativeBoolNode* cmpNode = FB_NEW_POOL(pool) ComparativeBoolNode(pool, blrOp,
|
2012-05-03 18:43:29 +02:00
|
|
|
doDsqlPass(dsqlScratch, arg1, false), rse->dsqlSelectList->items[0]);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2012-05-05 17:38:13 +02:00
|
|
|
PASS1_set_parameter_type(dsqlScratch, cmpNode->arg1, cmpNode->arg2, false);
|
|
|
|
|
|
|
|
rse->dsqlWhere = cmpNode;
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
// Create output node.
|
2017-09-27 17:55:08 +02:00
|
|
|
RseBoolNode* rseBoolNode = FB_NEW_POOL(pool) RseBoolNode(pool, rseBlrOp, rse);
|
2023-04-27 01:02:39 +02:00
|
|
|
rseBoolNode->line = line;
|
|
|
|
rseBoolNode->column = column;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
// Finish off by cleaning up contexts
|
|
|
|
dsqlScratch->unionContext.clear(baseUnion);
|
|
|
|
dsqlScratch->derivedContext.clear(baseDT);
|
|
|
|
dsqlScratch->context->clear(base);
|
|
|
|
|
2011-01-09 22:58:56 +01:00
|
|
|
return rseBoolNode;
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//--------------------
|
|
|
|
|
|
|
|
|
2023-09-04 08:13:10 +02:00
|
|
|
static RegisterBoolNode<InListBoolNode> regInListBoolNode({blr_in_list});
|
|
|
|
|
|
|
|
InListBoolNode::InListBoolNode(MemoryPool& pool, ValueExprNode* aArg, ValueListNode* aList)
|
|
|
|
: TypedNode<BoolExprNode, ExprNode::TYPE_IN_LIST_BOOL>(pool),
|
|
|
|
arg(aArg),
|
|
|
|
list(aList)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
DmlNode* InListBoolNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp)
|
|
|
|
{
|
|
|
|
const auto arg = PAR_parse_value(tdbb, csb);
|
|
|
|
|
|
|
|
const auto count = csb->csb_blr_reader.getWord();
|
|
|
|
const auto list = PAR_args(tdbb, csb, count, count);
|
|
|
|
|
|
|
|
return FB_NEW_POOL(pool) InListBoolNode(pool, arg, list);
|
|
|
|
}
|
|
|
|
|
|
|
|
string InListBoolNode::internalPrint(NodePrinter& printer) const
|
|
|
|
{
|
|
|
|
BoolExprNode::internalPrint(printer);
|
|
|
|
|
|
|
|
NODE_PRINT(printer, blrOp);
|
|
|
|
NODE_PRINT(printer, arg);
|
|
|
|
NODE_PRINT(printer, list);
|
|
|
|
|
|
|
|
return "InListBoolNode";
|
|
|
|
}
|
|
|
|
|
|
|
|
BoolExprNode* InListBoolNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
|
|
|
{
|
|
|
|
const auto procArg = doDsqlPass(dsqlScratch, arg);
|
|
|
|
const auto procList = doDsqlPass(dsqlScratch, list);
|
|
|
|
|
|
|
|
const auto node = FB_NEW_POOL(dsqlScratch->getPool())
|
|
|
|
InListBoolNode(dsqlScratch->getPool(), procArg, procList);
|
|
|
|
|
2023-09-26 12:58:47 +02:00
|
|
|
// Try to force arg to be same type as list eg: ? = (FIELD, ...) case
|
|
|
|
for (auto item : procList->items)
|
|
|
|
PASS1_set_parameter_type(dsqlScratch, node->arg, item, false);
|
|
|
|
|
|
|
|
// Try to force list to be same type as arg eg: FIELD = (?, ...) case
|
|
|
|
for (auto item : procList->items)
|
|
|
|
PASS1_set_parameter_type(dsqlScratch, item, node->arg, false);
|
|
|
|
|
|
|
|
// Derive a common data type for the list items
|
2023-09-04 08:13:10 +02:00
|
|
|
dsc argDesc;
|
|
|
|
DsqlDescMaker::fromNode(dsqlScratch, &argDesc, procArg);
|
|
|
|
|
|
|
|
dsc listDesc;
|
|
|
|
DsqlDescMaker::fromList(dsqlScratch, &listDesc, procList, "IN LIST");
|
|
|
|
|
|
|
|
if (argDesc.isText() && listDesc.isText())
|
|
|
|
{
|
|
|
|
const dsc* descs[] = {&argDesc, &listDesc};
|
|
|
|
dsc commonDesc;
|
|
|
|
DSqlDataTypeUtil(dsqlScratch).makeFromList(&commonDesc, "IN LIST",
|
|
|
|
FB_NELEM(descs), descs);
|
|
|
|
|
|
|
|
if (IS_INTL_DATA(&argDesc) || IS_INTL_DATA(&listDesc))
|
|
|
|
{
|
|
|
|
const auto charset1 = argDesc.getCharSet();
|
|
|
|
const auto charset2 = listDesc.getCharSet();
|
|
|
|
|
|
|
|
if ((charset1 != CS_BINARY) && (charset2 != CS_BINARY) &&
|
|
|
|
((charset1 != CS_ASCII) || (charset2 != CS_ASCII)) &&
|
|
|
|
((charset1 != CS_NONE) || (charset2 != CS_NONE)))
|
|
|
|
{
|
|
|
|
const auto ttype = MAX(argDesc.getTextType(), listDesc.getTextType());
|
|
|
|
commonDesc.setTextType(ttype);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
listDesc = commonDesc;
|
|
|
|
}
|
|
|
|
|
2023-09-26 12:58:47 +02:00
|
|
|
// Cast to the common data type where necessary
|
2023-09-04 08:13:10 +02:00
|
|
|
for (auto& item : procList->items)
|
|
|
|
{
|
|
|
|
const auto desc = item->getDsqlDesc();
|
|
|
|
|
|
|
|
if (!DSC_EQUIV(&listDesc, &desc, true))
|
|
|
|
{
|
|
|
|
auto field = FB_NEW_POOL(dsqlScratch->getPool())
|
|
|
|
dsql_fld(dsqlScratch->getPool());
|
|
|
|
|
|
|
|
field->dtype = listDesc.dsc_dtype;
|
|
|
|
field->scale = listDesc.dsc_scale;
|
|
|
|
field->subType = listDesc.dsc_sub_type;
|
|
|
|
field->length = listDesc.dsc_length;
|
|
|
|
field->flags = (listDesc.dsc_flags & DSC_nullable) ? FLD_nullable : 0;
|
|
|
|
|
|
|
|
if (desc.isText() || desc.isBlob())
|
|
|
|
{
|
|
|
|
field->textType = listDesc.getTextType();
|
|
|
|
field->charSetId = listDesc.getCharSet();
|
|
|
|
field->collationId = listDesc.getCollation();
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto castNode = FB_NEW_POOL(dsqlScratch->getPool())
|
|
|
|
CastNode(dsqlScratch->getPool(), item, field);
|
2024-04-17 21:36:09 +02:00
|
|
|
item = castNode;
|
2023-09-04 08:13:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
void InListBoolNode::genBlr(DsqlCompilerScratch* dsqlScratch)
|
|
|
|
{
|
|
|
|
dsqlScratch->appendUChar(blrOp);
|
|
|
|
|
|
|
|
GEN_expr(dsqlScratch, arg);
|
|
|
|
|
|
|
|
fb_assert(list->items.getCount() <= MAX_USHORT);
|
|
|
|
dsqlScratch->appendUShort(list->items.getCount());
|
|
|
|
|
|
|
|
for (auto item : list->items)
|
|
|
|
GEN_expr(dsqlScratch, item);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InListBoolNode::dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const
|
|
|
|
{
|
|
|
|
if (!BoolExprNode::dsqlMatch(dsqlScratch, other, ignoreMapCast))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return nodeIs<InListBoolNode>(other);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InListBoolNode::sameAs(const ExprNode* other, bool ignoreStreams) const
|
|
|
|
{
|
|
|
|
const auto otherNode = nodeAs<InListBoolNode>(other);
|
|
|
|
|
|
|
|
if (!otherNode)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return (arg->sameAs(otherNode->arg, ignoreStreams) &&
|
|
|
|
list->sameAs(otherNode->list, ignoreStreams));
|
|
|
|
}
|
|
|
|
|
|
|
|
BoolExprNode* InListBoolNode::copy(thread_db* tdbb, NodeCopier& copier) const
|
|
|
|
{
|
|
|
|
const auto newArg = copier.copy(tdbb, arg);
|
|
|
|
const auto newList = copier.copy(tdbb, list);
|
|
|
|
|
|
|
|
const auto node = FB_NEW_POOL(*tdbb->getDefaultPool())
|
|
|
|
InListBoolNode(*tdbb->getDefaultPool(), newArg, newList);
|
|
|
|
node->nodFlags = nodFlags;
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
BoolExprNode* InListBoolNode::pass1(thread_db* tdbb, CompilerScratch* csb)
|
|
|
|
{
|
|
|
|
doPass1(tdbb, csb, arg.getAddress());
|
|
|
|
|
|
|
|
nodFlags |= FLAG_INVARIANT;
|
|
|
|
csb->csb_current_nodes.push(this);
|
|
|
|
|
|
|
|
doPass1(tdbb, csb, list.getAddress());
|
|
|
|
|
|
|
|
csb->csb_current_nodes.pop();
|
|
|
|
|
|
|
|
if (nodFlags & FLAG_INVARIANT)
|
|
|
|
{
|
|
|
|
// If there is no top-level RSE present and list items are not constant, unmark node as invariant
|
|
|
|
// because it may be dependent on data or variables
|
|
|
|
|
|
|
|
for (const auto& ctxNode : csb->csb_current_nodes)
|
|
|
|
{
|
|
|
|
if (nodeIs<RseNode>(ctxNode))
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto item : list->items)
|
|
|
|
{
|
|
|
|
while (auto castNode = nodeAs<CastNode>(item))
|
|
|
|
item = castNode->source;
|
|
|
|
|
|
|
|
if (!nodeIs<LiteralNode>(item) && !nodeIs<ParameterNode>(item))
|
|
|
|
{
|
|
|
|
nodFlags &= ~FLAG_INVARIANT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
void InListBoolNode::pass2Boolean(thread_db* tdbb, CompilerScratch* csb, std::function<void ()> process)
|
|
|
|
{
|
|
|
|
if (nodFlags & FLAG_INVARIANT)
|
|
|
|
csb->csb_invariants.push(&impureOffset);
|
|
|
|
|
|
|
|
process();
|
|
|
|
|
|
|
|
if (const auto keyNode = nodeAs<RecordKeyNode>(arg))
|
|
|
|
{
|
|
|
|
if (keyNode->aggregate)
|
|
|
|
ERR_post(Arg::Gds(isc_bad_dbkey));
|
|
|
|
}
|
|
|
|
|
2024-05-01 11:46:34 +02:00
|
|
|
dsc argDesc, listDesc;
|
|
|
|
arg->getDesc(tdbb, csb, &argDesc);
|
|
|
|
list->getDesc(tdbb, csb, &listDesc);
|
2023-09-04 08:13:10 +02:00
|
|
|
|
2024-05-01 11:46:34 +02:00
|
|
|
if (argDesc.isDateTime())
|
2023-09-04 08:13:10 +02:00
|
|
|
arg->nodFlags |= FLAG_DATE;
|
2024-05-01 11:46:34 +02:00
|
|
|
else if (listDesc.isDateTime())
|
2023-09-04 08:13:10 +02:00
|
|
|
{
|
|
|
|
for (auto item : list->items)
|
|
|
|
item->nodFlags |= FLAG_DATE;
|
|
|
|
}
|
|
|
|
|
2024-05-01 11:46:34 +02:00
|
|
|
// If lookup in the list is to be performed against the generic comparison rules,
|
|
|
|
// add an extra cast to make things working properly
|
|
|
|
if (!BTR_types_comparable(listDesc, argDesc))
|
|
|
|
{
|
|
|
|
for (auto& item : list->items)
|
|
|
|
{
|
|
|
|
const auto castNode = FB_NEW_POOL(csb->csb_pool) CastNode(csb->csb_pool);
|
|
|
|
castNode->castDesc = argDesc;
|
|
|
|
castNode->source = item;
|
|
|
|
castNode->impureOffset = csb->allocImpure<impure_value>();
|
|
|
|
item = castNode;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-04 08:13:10 +02:00
|
|
|
if (nodFlags & FLAG_INVARIANT)
|
|
|
|
impureOffset = csb->allocImpure<impure_value>();
|
2023-09-07 19:55:48 +02:00
|
|
|
|
|
|
|
lookup = FB_NEW_POOL(csb->csb_pool) LookupValueList(csb->csb_pool, list, impureOffset);
|
2023-09-04 08:13:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool InListBoolNode::execute(thread_db* tdbb, Request* request) const
|
|
|
|
{
|
|
|
|
if (const auto argDesc = EVL_expr(tdbb, request, arg))
|
|
|
|
{
|
2023-10-10 20:46:16 +02:00
|
|
|
bool anyMatch = false, anyNull = false;
|
|
|
|
|
2023-09-04 08:13:10 +02:00
|
|
|
if (nodFlags & FLAG_INVARIANT)
|
|
|
|
{
|
2023-10-10 20:46:16 +02:00
|
|
|
anyMatch = lookup->find(tdbb, request, arg, argDesc);
|
|
|
|
anyNull = (request->req_flags & req_null);
|
2023-09-04 08:13:10 +02:00
|
|
|
}
|
2023-10-10 20:46:16 +02:00
|
|
|
else
|
2023-09-04 08:13:10 +02:00
|
|
|
{
|
2023-10-10 20:46:16 +02:00
|
|
|
for (const auto value : list->items)
|
2023-09-04 08:13:10 +02:00
|
|
|
{
|
2023-10-10 20:46:16 +02:00
|
|
|
if (const auto valueDesc = EVL_expr(tdbb, request, value))
|
|
|
|
{
|
|
|
|
if (!MOV_compare(tdbb, argDesc, valueDesc))
|
|
|
|
{
|
|
|
|
anyMatch = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
anyNull = true;
|
|
|
|
}
|
2023-09-04 08:13:10 +02:00
|
|
|
}
|
|
|
|
}
|
2023-10-10 20:46:16 +02:00
|
|
|
|
|
|
|
request->req_flags &= ~req_null;
|
|
|
|
|
|
|
|
if (anyMatch)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (anyNull)
|
|
|
|
request->req_flags |= req_null;
|
2023-09-04 08:13:10 +02:00
|
|
|
}
|
|
|
|
|
2023-10-10 20:46:16 +02:00
|
|
|
return false; // for argDesc == nullptr, req_null is already set by EVL_expr()
|
2023-09-04 08:13:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//--------------------
|
|
|
|
|
|
|
|
|
2020-06-05 18:57:46 +02:00
|
|
|
static RegisterBoolNode<MissingBoolNode> regMissingBoolNode({blr_missing});
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
MissingBoolNode::MissingBoolNode(MemoryPool& pool, ValueExprNode* aArg, bool aDsqlUnknown)
|
2010-09-17 05:15:32 +02:00
|
|
|
: TypedNode<BoolExprNode, ExprNode::TYPE_MISSING_BOOL>(pool),
|
2010-12-27 01:34:31 +01:00
|
|
|
dsqlUnknown(aDsqlUnknown),
|
2012-05-03 18:43:29 +02:00
|
|
|
arg(aArg)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-04-09 04:28:38 +02:00
|
|
|
DmlNode* MissingBoolNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR /*blrOp*/)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2015-10-12 16:26:00 +02:00
|
|
|
MissingBoolNode* node = FB_NEW_POOL(pool) MissingBoolNode(pool);
|
2010-11-21 04:47:29 +01:00
|
|
|
node->arg = PAR_parse_value(tdbb, csb);
|
2010-09-17 05:15:32 +02:00
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2015-06-05 20:26:36 +02:00
|
|
|
string MissingBoolNode::internalPrint(NodePrinter& printer) const
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2015-06-05 20:26:36 +02:00
|
|
|
BoolExprNode::internalPrint(printer);
|
|
|
|
|
|
|
|
NODE_PRINT(printer, dsqlUnknown);
|
|
|
|
NODE_PRINT(printer, arg);
|
|
|
|
|
|
|
|
return "MissingBoolNode";
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
BoolExprNode* MissingBoolNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
|
|
|
{
|
2017-09-27 17:55:08 +02:00
|
|
|
MissingBoolNode* node = FB_NEW_POOL(dsqlScratch->getPool()) MissingBoolNode(dsqlScratch->getPool(),
|
2012-05-03 18:43:29 +02:00
|
|
|
doDsqlPass(dsqlScratch, arg));
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2018-11-28 11:52:18 +01:00
|
|
|
// dimitr: MSVC12 has a known bug with default function constructor. MSVC13 seems to have it fixed,
|
|
|
|
// but I keep the explicit empty-object initializer here.
|
|
|
|
PASS1_set_parameter_type(dsqlScratch, node->arg, std::function<void (dsc*)>(nullptr), false);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2010-12-27 01:34:31 +01:00
|
|
|
dsc desc;
|
2019-05-23 17:55:06 +02:00
|
|
|
DsqlDescMaker::fromNode(dsqlScratch, &desc, node->arg);
|
2010-12-27 01:34:31 +01:00
|
|
|
|
|
|
|
if (dsqlUnknown && desc.dsc_dtype != dtype_boolean && !desc.isNull())
|
|
|
|
{
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
Arg::Gds(isc_invalid_boolean_usage));
|
|
|
|
}
|
|
|
|
|
2010-09-17 05:15:32 +02:00
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MissingBoolNode::genBlr(DsqlCompilerScratch* dsqlScratch)
|
|
|
|
{
|
|
|
|
dsqlScratch->appendUChar(blr_missing);
|
2012-05-03 18:43:29 +02:00
|
|
|
GEN_expr(dsqlScratch, arg);
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
2011-02-06 22:59:20 +01:00
|
|
|
BoolExprNode* MissingBoolNode::copy(thread_db* tdbb, NodeCopier& copier) const
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2015-10-12 16:26:00 +02:00
|
|
|
MissingBoolNode* node = FB_NEW_POOL(*tdbb->getDefaultPool()) MissingBoolNode(
|
2010-09-17 05:15:32 +02:00
|
|
|
*tdbb->getDefaultPool());
|
2010-11-14 23:31:42 +01:00
|
|
|
node->nodFlags = nodFlags;
|
2010-09-17 05:15:32 +02:00
|
|
|
node->arg = copier.copy(tdbb, arg);
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2010-09-20 18:07:50 +02:00
|
|
|
BoolExprNode* MissingBoolNode::pass1(thread_db* tdbb, CompilerScratch* csb)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
|
|
|
return BoolExprNode::pass1(tdbb, csb);
|
|
|
|
}
|
|
|
|
|
2023-04-27 01:02:39 +02:00
|
|
|
void MissingBoolNode::pass2Boolean(thread_db* tdbb, CompilerScratch* csb, std::function<void ()> process)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2023-04-27 01:02:39 +02:00
|
|
|
process();
|
|
|
|
|
2017-06-09 19:09:36 +02:00
|
|
|
RecordKeyNode* keyNode = nodeAs<RecordKeyNode>(arg);
|
2010-11-14 18:25:48 +01:00
|
|
|
|
2011-04-02 06:24:20 +02:00
|
|
|
if (keyNode && keyNode->aggregate)
|
2010-09-17 05:15:32 +02:00
|
|
|
ERR_post(Arg::Gds(isc_bad_dbkey));
|
|
|
|
|
|
|
|
// check for syntax errors in the calculation
|
|
|
|
dsc descriptor_a;
|
2010-11-21 04:47:29 +01:00
|
|
|
arg->getDesc(tdbb, csb, &descriptor_a);
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
2022-02-13 14:51:30 +01:00
|
|
|
bool MissingBoolNode::execute(thread_db* tdbb, Request* request) const
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2010-11-21 04:47:29 +01:00
|
|
|
EVL_expr(tdbb, request, arg);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
if (request->req_flags & req_null)
|
|
|
|
{
|
|
|
|
request->req_flags &= ~req_null;
|
|
|
|
return true;
|
|
|
|
}
|
2010-09-24 10:33:22 +02:00
|
|
|
|
|
|
|
return false;
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//--------------------
|
|
|
|
|
|
|
|
|
2020-06-05 18:57:46 +02:00
|
|
|
static RegisterBoolNode<NotBoolNode> regNotBoolNode({blr_not});
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
NotBoolNode::NotBoolNode(MemoryPool& pool, BoolExprNode* aArg)
|
2010-09-17 05:15:32 +02:00
|
|
|
: TypedNode<BoolExprNode, ExprNode::TYPE_NOT_BOOL>(pool),
|
2012-05-03 18:43:29 +02:00
|
|
|
arg(aArg)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-04-09 04:28:38 +02:00
|
|
|
DmlNode* NotBoolNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR /*blrOp*/)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2015-10-12 16:26:00 +02:00
|
|
|
NotBoolNode* node = FB_NEW_POOL(pool) NotBoolNode(pool);
|
2010-09-20 18:07:50 +02:00
|
|
|
node->arg = PAR_parse_boolean(tdbb, csb);
|
2010-09-17 05:15:32 +02:00
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2015-06-05 20:26:36 +02:00
|
|
|
string NotBoolNode::internalPrint(NodePrinter& printer) const
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2015-06-05 20:26:36 +02:00
|
|
|
BoolExprNode::internalPrint(printer);
|
|
|
|
|
|
|
|
NODE_PRINT(printer, arg);
|
|
|
|
|
|
|
|
return "NotBoolNode";
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
BoolExprNode* NotBoolNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
|
|
|
{
|
|
|
|
return process(dsqlScratch, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void NotBoolNode::genBlr(DsqlCompilerScratch* dsqlScratch)
|
|
|
|
{
|
|
|
|
dsqlScratch->appendUChar(blr_not);
|
2012-05-03 18:43:29 +02:00
|
|
|
GEN_expr(dsqlScratch, arg);
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
2011-02-06 22:59:20 +01:00
|
|
|
BoolExprNode* NotBoolNode::copy(thread_db* tdbb, NodeCopier& copier) const
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2015-10-12 16:26:00 +02:00
|
|
|
NotBoolNode* node = FB_NEW_POOL(*tdbb->getDefaultPool()) NotBoolNode(*tdbb->getDefaultPool());
|
2010-11-14 23:31:42 +01:00
|
|
|
node->nodFlags = nodFlags;
|
2010-11-21 04:47:29 +01:00
|
|
|
node->arg = copier.copy(tdbb, arg);
|
2010-09-17 05:15:32 +02:00
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2010-09-20 18:07:50 +02:00
|
|
|
BoolExprNode* NotBoolNode::pass1(thread_db* tdbb, CompilerScratch* csb)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2023-10-14 18:04:38 +02:00
|
|
|
RseBoolNode* rseBoolean = nodeAs<RseBoolNode>(arg);
|
|
|
|
|
|
|
|
if (rseBoolean)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
|
|
|
if (rseBoolean->blrOp == blr_ansi_any)
|
2010-11-14 23:31:42 +01:00
|
|
|
rseBoolean->nodFlags |= FLAG_DEOPTIMIZE | FLAG_ANSI_NOT;
|
2010-09-24 10:33:22 +02:00
|
|
|
else if (rseBoolean->blrOp == blr_ansi_all)
|
2010-11-14 23:31:42 +01:00
|
|
|
rseBoolean->nodFlags |= FLAG_ANSI_NOT;
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return BoolExprNode::pass1(tdbb, csb);
|
|
|
|
}
|
|
|
|
|
2022-02-13 14:51:30 +01:00
|
|
|
bool NotBoolNode::execute(thread_db* tdbb, Request* request) const
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2010-09-20 18:07:50 +02:00
|
|
|
bool value = arg->execute(tdbb, request);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
if (request->req_flags & req_null)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return !value;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Replace NOT with an appropriately inverted condition, if possible.
|
|
|
|
// Get rid of redundant nested NOT predicates.
|
|
|
|
BoolExprNode* NotBoolNode::process(DsqlCompilerScratch* dsqlScratch, bool invert)
|
|
|
|
{
|
2017-09-27 17:55:08 +02:00
|
|
|
MemoryPool& pool = dsqlScratch->getPool();
|
2017-06-09 19:09:36 +02:00
|
|
|
NotBoolNode* notArg = nodeAs<NotBoolNode>(arg);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
if (notArg)
|
|
|
|
{
|
|
|
|
// Recurse until different node is found (every even call means no inversion required).
|
|
|
|
return notArg->process(dsqlScratch, !invert);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!invert)
|
2012-05-03 18:43:29 +02:00
|
|
|
return arg->dsqlPass(dsqlScratch);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2024-02-02 11:07:28 +01:00
|
|
|
const auto cmpArg = nodeAs<ComparativeBoolNode>(arg);
|
|
|
|
const auto binArg = nodeAs<BinaryBoolNode>(arg);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
// Do not handle special case: <value> NOT IN <list>
|
|
|
|
|
2017-06-09 19:09:36 +02:00
|
|
|
if (cmpArg && (!cmpArg->dsqlSpecialArg || !nodeIs<ValueListNode>(cmpArg->dsqlSpecialArg)))
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2024-02-02 11:07:28 +01:00
|
|
|
// Invert the given boolean
|
|
|
|
|
|
|
|
// For (A = TRUE/FALSE), invert only the boolean value, not the condition itself
|
|
|
|
|
|
|
|
if (cmpArg->blrOp == blr_eql)
|
|
|
|
{
|
|
|
|
auto newArg1 = cmpArg->arg1;
|
|
|
|
auto newArg2 = cmpArg->arg2;
|
|
|
|
|
|
|
|
if (const auto literal = nodeAs<LiteralNode>(cmpArg->arg1))
|
|
|
|
{
|
|
|
|
if (literal->litDesc.isBoolean())
|
|
|
|
{
|
|
|
|
const auto invertedVal = literal->getBoolean() ? "" : "1";
|
|
|
|
newArg1 = MAKE_constant(invertedVal, CONSTANT_BOOLEAN);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (const auto literal = nodeAs<LiteralNode>(cmpArg->arg2))
|
|
|
|
{
|
|
|
|
if (literal->litDesc.isBoolean())
|
|
|
|
{
|
|
|
|
const auto invertedVal = literal->getBoolean() ? "" : "1";
|
|
|
|
newArg2 = MAKE_constant(invertedVal, CONSTANT_BOOLEAN);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cmpArg->arg1 != newArg1 || cmpArg->arg2 != newArg2)
|
|
|
|
{
|
|
|
|
ComparativeBoolNode* node = FB_NEW_POOL(pool) ComparativeBoolNode(
|
|
|
|
pool, cmpArg->blrOp, newArg1, newArg2);
|
|
|
|
node->dsqlSpecialArg = cmpArg->dsqlSpecialArg;
|
|
|
|
node->dsqlCheckBoolean = cmpArg->dsqlCheckBoolean;
|
|
|
|
|
|
|
|
return node->dsqlPass(dsqlScratch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-17 05:15:32 +02:00
|
|
|
switch (cmpArg->blrOp)
|
|
|
|
{
|
|
|
|
case blr_eql:
|
|
|
|
case blr_neq:
|
|
|
|
case blr_lss:
|
|
|
|
case blr_gtr:
|
|
|
|
case blr_leq:
|
|
|
|
case blr_geq:
|
|
|
|
{
|
|
|
|
UCHAR newBlrOp;
|
|
|
|
|
|
|
|
switch (cmpArg->blrOp)
|
|
|
|
{
|
|
|
|
case blr_eql:
|
|
|
|
newBlrOp = blr_neq;
|
|
|
|
break;
|
|
|
|
case blr_neq:
|
|
|
|
newBlrOp = blr_eql;
|
|
|
|
break;
|
|
|
|
case blr_lss:
|
|
|
|
newBlrOp = blr_geq;
|
|
|
|
break;
|
|
|
|
case blr_gtr:
|
|
|
|
newBlrOp = blr_leq;
|
|
|
|
break;
|
|
|
|
case blr_leq:
|
|
|
|
newBlrOp = blr_gtr;
|
|
|
|
break;
|
|
|
|
case blr_geq:
|
|
|
|
newBlrOp = blr_lss;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fb_assert(false);
|
2014-02-18 02:49:07 +01:00
|
|
|
return NULL;
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
2017-09-27 17:55:08 +02:00
|
|
|
ComparativeBoolNode* node = FB_NEW_POOL(pool) ComparativeBoolNode(
|
|
|
|
pool, newBlrOp, cmpArg->arg1, cmpArg->arg2);
|
2012-05-03 18:43:29 +02:00
|
|
|
node->dsqlSpecialArg = cmpArg->dsqlSpecialArg;
|
2016-04-04 19:49:44 +02:00
|
|
|
node->dsqlCheckBoolean = cmpArg->dsqlCheckBoolean;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
if (cmpArg->dsqlFlag == ComparativeBoolNode::DFLAG_ANSI_ANY)
|
|
|
|
node->dsqlFlag = ComparativeBoolNode::DFLAG_ANSI_ALL;
|
|
|
|
else if (cmpArg->dsqlFlag == ComparativeBoolNode::DFLAG_ANSI_ALL)
|
|
|
|
node->dsqlFlag = ComparativeBoolNode::DFLAG_ANSI_ANY;
|
|
|
|
|
|
|
|
return node->dsqlPass(dsqlScratch);
|
|
|
|
}
|
|
|
|
|
|
|
|
case blr_between:
|
|
|
|
{
|
2017-09-27 17:55:08 +02:00
|
|
|
ComparativeBoolNode* cmpNode1 = FB_NEW_POOL(pool) ComparativeBoolNode(pool,
|
2012-05-03 18:43:29 +02:00
|
|
|
blr_lss, cmpArg->arg1, cmpArg->arg2);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2017-09-27 17:55:08 +02:00
|
|
|
ComparativeBoolNode* cmpNode2 = FB_NEW_POOL(pool) ComparativeBoolNode(pool,
|
2012-05-03 18:43:29 +02:00
|
|
|
blr_gtr, cmpArg->arg1, cmpArg->arg3);
|
2012-04-25 03:42:47 +02:00
|
|
|
|
2017-09-27 17:55:08 +02:00
|
|
|
BinaryBoolNode* node = FB_NEW_POOL(pool) BinaryBoolNode(pool, blr_or,
|
2012-04-25 03:42:47 +02:00
|
|
|
cmpNode1, cmpNode2);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
return node->dsqlPass(dsqlScratch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (binArg)
|
|
|
|
{
|
|
|
|
switch (binArg->blrOp)
|
|
|
|
{
|
|
|
|
case blr_and:
|
|
|
|
case blr_or:
|
|
|
|
{
|
|
|
|
UCHAR newBlrOp = binArg->blrOp == blr_and ? blr_or : blr_and;
|
|
|
|
|
2017-09-27 17:55:08 +02:00
|
|
|
NotBoolNode* notNode1 = FB_NEW_POOL(pool) NotBoolNode(pool, binArg->arg1);
|
|
|
|
NotBoolNode* notNode2 = FB_NEW_POOL(pool) NotBoolNode(pool, binArg->arg2);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2017-09-27 17:55:08 +02:00
|
|
|
BinaryBoolNode* node = FB_NEW_POOL(pool) BinaryBoolNode(pool, newBlrOp,
|
2012-04-25 03:42:47 +02:00
|
|
|
notNode1, notNode2);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
return node->dsqlPass(dsqlScratch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// No inversion is possible, so just recreate the input node
|
|
|
|
// and return immediately to avoid infinite recursion later.
|
|
|
|
|
2017-09-27 17:55:08 +02:00
|
|
|
return FB_NEW_POOL(pool) NotBoolNode(pool, doDsqlPass(dsqlScratch, arg));
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//--------------------
|
|
|
|
|
|
|
|
|
2020-06-05 18:57:46 +02:00
|
|
|
// ASF: Where is blr_exists handled?
|
|
|
|
static RegisterBoolNode<RseBoolNode> regRseBoolNode({blr_any, blr_unique, blr_ansi_any, blr_ansi_all, blr_exists});
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2012-04-25 03:42:47 +02:00
|
|
|
RseBoolNode::RseBoolNode(MemoryPool& pool, UCHAR aBlrOp, RecordSourceNode* aDsqlRse)
|
2010-09-17 05:15:32 +02:00
|
|
|
: TypedNode<BoolExprNode, ExprNode::TYPE_RSE_BOOL>(pool),
|
|
|
|
blrOp(aBlrOp),
|
2014-03-31 04:04:26 +02:00
|
|
|
ownSavepoint(true),
|
2023-04-27 01:02:39 +02:00
|
|
|
dsqlRse(aDsqlRse)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-04-09 04:28:38 +02:00
|
|
|
DmlNode* RseBoolNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2015-10-12 16:26:00 +02:00
|
|
|
RseBoolNode* node = FB_NEW_POOL(pool) RseBoolNode(pool, blrOp);
|
2010-11-21 04:47:29 +01:00
|
|
|
node->rse = PAR_rse(tdbb, csb);
|
2012-09-24 16:26:33 +02:00
|
|
|
|
2022-12-31 20:46:12 +01:00
|
|
|
node->rse->flags |= RseNode::FLAG_SUB_QUERY;
|
|
|
|
|
2014-01-05 20:40:07 +01:00
|
|
|
if (blrOp == blr_any || blrOp == blr_exists) // maybe for blr_unique as well?
|
2023-09-13 20:21:12 +02:00
|
|
|
node->rse->firstRows = true;
|
2014-01-05 20:40:07 +01:00
|
|
|
|
2012-09-24 16:26:33 +02:00
|
|
|
if (csb->csb_currentForNode && csb->csb_currentForNode->parBlrBeginCnt <= 1)
|
2014-03-19 23:31:50 +01:00
|
|
|
node->ownSavepoint = false;
|
2012-09-24 16:26:33 +02:00
|
|
|
|
2016-02-25 10:20:01 +01:00
|
|
|
if (csb->csb_currentDMLNode)
|
|
|
|
node->ownSavepoint = false;
|
|
|
|
|
2010-09-17 05:15:32 +02:00
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2015-06-05 20:26:36 +02:00
|
|
|
string RseBoolNode::internalPrint(NodePrinter& printer) const
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2015-06-05 20:26:36 +02:00
|
|
|
BoolExprNode::internalPrint(printer);
|
|
|
|
|
|
|
|
NODE_PRINT(printer, blrOp);
|
|
|
|
NODE_PRINT(printer, ownSavepoint);
|
|
|
|
NODE_PRINT(printer, dsqlRse);
|
|
|
|
NODE_PRINT(printer, rse);
|
2015-08-31 17:11:06 +02:00
|
|
|
NODE_PRINT(printer, subQuery);
|
2015-06-05 20:26:36 +02:00
|
|
|
|
|
|
|
return "RseBoolNode";
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
BoolExprNode* RseBoolNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
|
|
|
{
|
2016-03-09 19:45:38 +01:00
|
|
|
if (dsqlScratch->flags & DsqlCompilerScratch::FLAG_VIEW_WITH_CHECK)
|
|
|
|
{
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-607) <<
|
|
|
|
Arg::Gds(isc_subquery_err));
|
|
|
|
}
|
|
|
|
|
2010-09-17 05:15:32 +02:00
|
|
|
const DsqlContextStack::iterator base(*dsqlScratch->context);
|
|
|
|
|
2017-09-27 17:55:08 +02:00
|
|
|
RseBoolNode* node = FB_NEW_POOL(dsqlScratch->getPool()) RseBoolNode(dsqlScratch->getPool(), blrOp,
|
2023-09-13 20:21:12 +02:00
|
|
|
PASS1_rse(dsqlScratch, nodeAs<SelectExprNode>(dsqlRse)));
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
// Finish off by cleaning up contexts
|
|
|
|
dsqlScratch->context->clear(base);
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RseBoolNode::genBlr(DsqlCompilerScratch* dsqlScratch)
|
|
|
|
{
|
|
|
|
dsqlScratch->appendUChar(blrOp);
|
2023-04-27 01:02:39 +02:00
|
|
|
|
|
|
|
dsqlScratch->putDebugSrcInfo(line, column);
|
2017-06-09 19:09:36 +02:00
|
|
|
GEN_rse(dsqlScratch, nodeAs<RseNode>(dsqlRse));
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
2017-10-02 18:31:00 +02:00
|
|
|
bool RseBoolNode::dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2017-10-02 18:31:00 +02:00
|
|
|
if (!BoolExprNode::dsqlMatch(dsqlScratch, other, ignoreMapCast))
|
2010-09-17 05:15:32 +02:00
|
|
|
return false;
|
|
|
|
|
2017-06-09 19:09:36 +02:00
|
|
|
const RseBoolNode* o = nodeAs<RseBoolNode>(other);
|
2010-09-24 10:33:22 +02:00
|
|
|
fb_assert(o);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
return blrOp == o->blrOp;
|
|
|
|
}
|
|
|
|
|
2021-10-12 19:17:17 +02:00
|
|
|
bool RseBoolNode::sameAs(const ExprNode* other, bool ignoreStreams) const
|
2010-09-20 18:07:50 +02:00
|
|
|
{
|
2021-10-12 19:17:17 +02:00
|
|
|
if (!BoolExprNode::sameAs(other, ignoreStreams))
|
2010-09-20 18:07:50 +02:00
|
|
|
return false;
|
|
|
|
|
2017-06-09 19:09:36 +02:00
|
|
|
const RseBoolNode* const otherNode = nodeAs<RseBoolNode>(other);
|
2010-09-20 18:07:50 +02:00
|
|
|
fb_assert(otherNode);
|
|
|
|
|
|
|
|
return blrOp == otherNode->blrOp;
|
|
|
|
}
|
|
|
|
|
2011-02-06 22:59:20 +01:00
|
|
|
BoolExprNode* RseBoolNode::copy(thread_db* tdbb, NodeCopier& copier) const
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2015-10-12 16:26:00 +02:00
|
|
|
RseBoolNode* node = FB_NEW_POOL(*tdbb->getDefaultPool()) RseBoolNode(
|
2010-09-17 05:15:32 +02:00
|
|
|
*tdbb->getDefaultPool(), blrOp);
|
2010-11-14 23:31:42 +01:00
|
|
|
node->nodFlags = nodFlags;
|
2014-03-19 23:31:50 +01:00
|
|
|
node->ownSavepoint = this->ownSavepoint;
|
2010-09-17 05:15:32 +02:00
|
|
|
node->rse = copier.copy(tdbb, rse);
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2010-09-20 18:07:50 +02:00
|
|
|
BoolExprNode* RseBoolNode::pass1(thread_db* tdbb, CompilerScratch* csb)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
|
|
|
switch (blrOp)
|
|
|
|
{
|
|
|
|
case blr_ansi_all:
|
|
|
|
{
|
|
|
|
BoolExprNode* newNode = convertNeqAllToNotAny(tdbb, csb);
|
|
|
|
if (newNode)
|
2010-12-04 23:15:03 +01:00
|
|
|
{
|
|
|
|
doPass1(tdbb, csb, &newNode);
|
|
|
|
return newNode;
|
|
|
|
}
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2010-11-14 23:31:42 +01:00
|
|
|
nodFlags |= FLAG_DEOPTIMIZE;
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
// fall into
|
|
|
|
|
|
|
|
case blr_ansi_any:
|
2011-02-26 10:31:46 +01:00
|
|
|
{
|
|
|
|
bool deoptimize = false;
|
|
|
|
|
2010-11-14 23:31:42 +01:00
|
|
|
if (nodFlags & FLAG_DEOPTIMIZE)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2011-02-26 10:31:46 +01:00
|
|
|
nodFlags &= ~FLAG_DEOPTIMIZE;
|
|
|
|
deoptimize = true;
|
|
|
|
}
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2011-02-26 10:31:46 +01:00
|
|
|
// Mark the injected boolean as residual, this is required
|
|
|
|
// to process quantified predicates correctly in some cases.
|
|
|
|
//
|
|
|
|
// If necessary, also deoptimize the injected boolean.
|
|
|
|
// ALL predicate should not be evaluated using an index scan.
|
|
|
|
// This fixes bug SF #543106.
|
|
|
|
|
|
|
|
BoolExprNode* boolean = rse->rse_boolean;
|
|
|
|
if (boolean)
|
|
|
|
{
|
2017-06-09 19:09:36 +02:00
|
|
|
BinaryBoolNode* const binaryNode = nodeAs<BinaryBoolNode>(boolean);
|
2011-02-26 10:31:46 +01:00
|
|
|
if (binaryNode && binaryNode->blrOp == blr_and)
|
|
|
|
boolean = binaryNode->arg2;
|
|
|
|
|
|
|
|
boolean->nodFlags |= FLAG_RESIDUAL | (deoptimize ? FLAG_DEOPTIMIZE : 0);
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
2011-02-26 10:31:46 +01:00
|
|
|
}
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2020-06-15 18:22:26 +02:00
|
|
|
break;
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return BoolExprNode::pass1(tdbb, csb);
|
|
|
|
}
|
|
|
|
|
2023-04-27 01:02:39 +02:00
|
|
|
void RseBoolNode::pass2Boolean(thread_db* tdbb, CompilerScratch* csb, std::function<void ()> process)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2023-01-04 10:08:11 +01:00
|
|
|
if (rse->isInvariant())
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2010-11-14 23:31:42 +01:00
|
|
|
nodFlags |= FLAG_INVARIANT;
|
2010-09-20 18:07:50 +02:00
|
|
|
csb->csb_invariants.push(&impureOffset);
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
2023-07-26 13:02:05 +02:00
|
|
|
AutoSetCurrentCursorId autoSetCurrentCursorId(csb);
|
2023-04-27 01:02:39 +02:00
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
rse->pass2Rse(tdbb, csb);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2023-04-27 01:02:39 +02:00
|
|
|
process();
|
|
|
|
|
2010-11-14 23:31:42 +01:00
|
|
|
if (nodFlags & FLAG_INVARIANT)
|
2020-06-11 17:55:00 +02:00
|
|
|
impureOffset = csb->allocImpure<impure_value>();
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2015-08-31 17:11:06 +02:00
|
|
|
RecordSource* const rsb = CMP_post_rse(tdbb, csb, rse);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
|
|
|
// for ansi ANY clauses (and ALL's, which are negated ANY's)
|
|
|
|
// the unoptimized boolean expression must be used, since the
|
|
|
|
// processing of these clauses is order dependant (see FilteredStream.cpp)
|
|
|
|
|
2011-02-26 10:31:46 +01:00
|
|
|
if (blrOp == blr_ansi_any || blrOp == blr_ansi_all)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2010-09-20 18:07:50 +02:00
|
|
|
const bool ansiAny = blrOp == blr_ansi_any;
|
2010-11-14 23:31:42 +01:00
|
|
|
const bool ansiNot = nodFlags & FLAG_ANSI_NOT;
|
2011-02-26 08:25:10 +01:00
|
|
|
rsb->setAnyBoolean(rse->rse_boolean, ansiAny, ansiNot);
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
2023-07-30 04:49:21 +02:00
|
|
|
subQuery = FB_NEW_POOL(*tdbb->getDefaultPool()) SubQuery(csb, rsb, rse);
|
2022-12-31 20:46:12 +01:00
|
|
|
csb->csb_fors.add(subQuery);
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
2022-02-13 14:51:30 +01:00
|
|
|
bool RseBoolNode::execute(thread_db* tdbb, Request* request) const
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
|
|
|
USHORT* invariant_flags;
|
|
|
|
impure_value* impure;
|
|
|
|
|
2010-11-14 23:31:42 +01:00
|
|
|
if (nodFlags & FLAG_INVARIANT)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2010-09-20 18:07:50 +02:00
|
|
|
impure = request->getImpure<impure_value>(impureOffset);
|
2010-09-17 05:15:32 +02:00
|
|
|
invariant_flags = &impure->vlu_flags;
|
|
|
|
|
|
|
|
if (*invariant_flags & VLU_computed)
|
|
|
|
{
|
|
|
|
// An invariant node has already been computed.
|
|
|
|
|
|
|
|
if (blrOp == blr_ansi_any && (*invariant_flags & VLU_null))
|
|
|
|
request->req_flags |= req_null;
|
|
|
|
else
|
|
|
|
request->req_flags &= ~req_null;
|
|
|
|
|
|
|
|
return impure->vlu_misc.vlu_short != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-19 23:31:50 +01:00
|
|
|
StableCursorSavePoint savePoint(tdbb, request->req_transaction, ownSavepoint);
|
2012-09-24 16:26:33 +02:00
|
|
|
|
2020-11-04 07:35:01 +01:00
|
|
|
bool value;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
subQuery->open(tdbb);
|
|
|
|
value = subQuery->fetch(tdbb);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2020-11-04 07:35:01 +01:00
|
|
|
if (blrOp == blr_unique && value)
|
|
|
|
value = !subQuery->fetch(tdbb);
|
|
|
|
}
|
|
|
|
catch (const Exception&)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
subQuery->close(tdbb);
|
|
|
|
}
|
|
|
|
catch (const Exception&)
|
|
|
|
{} // ignore any error to report the original one
|
|
|
|
|
|
|
|
throw;
|
|
|
|
}
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2015-08-31 17:11:06 +02:00
|
|
|
subQuery->close(tdbb);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2020-09-25 10:15:31 +02:00
|
|
|
savePoint.release();
|
|
|
|
|
2010-09-17 05:15:32 +02:00
|
|
|
if (blrOp == blr_any || blrOp == blr_unique)
|
|
|
|
request->req_flags &= ~req_null;
|
|
|
|
|
|
|
|
// If this is an invariant node, save the return value.
|
|
|
|
|
2010-11-14 23:31:42 +01:00
|
|
|
if (nodFlags & FLAG_INVARIANT)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
|
|
|
*invariant_flags |= VLU_computed;
|
|
|
|
|
|
|
|
if ((blrOp == blr_ansi_any || blrOp == blr_ansi_all) && (request->req_flags & req_null))
|
|
|
|
*invariant_flags |= VLU_null;
|
|
|
|
|
|
|
|
impure->vlu_misc.vlu_short = value ? TRUE : FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to convert nodes of expression:
|
|
|
|
// select ... from <t1>
|
|
|
|
// where <x> not in (select <y> from <t2>)
|
|
|
|
// (and its variants that uses the same BLR: {NOT (a = ANY b)} and {a <> ALL b})
|
|
|
|
// to:
|
|
|
|
// select ... from <t1>
|
|
|
|
// where not ((x is null and exists (select 1 from <t2>)) or
|
|
|
|
// exists (select <y> from <t2> where <y> = <x> or <y> is null))
|
|
|
|
//
|
|
|
|
// Because the second form can use indexes.
|
|
|
|
// Returns NULL when not converted, and a new node to be processed when converted.
|
|
|
|
BoolExprNode* RseBoolNode::convertNeqAllToNotAny(thread_db* tdbb, CompilerScratch* csb)
|
|
|
|
{
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
|
|
|
fb_assert(blrOp == blr_ansi_all);
|
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
RseNode* outerRse = rse; // blr_ansi_all rse
|
2010-09-17 05:15:32 +02:00
|
|
|
ComparativeBoolNode* outerRseNeq;
|
|
|
|
|
2020-06-06 21:36:08 +02:00
|
|
|
if (!outerRse ||
|
|
|
|
outerRse->getType() != RseNode::TYPE || // Reduntant test?
|
|
|
|
outerRse->rse_relations.getCount() != 1 ||
|
2010-09-17 05:15:32 +02:00
|
|
|
!outerRse->rse_boolean ||
|
2017-06-09 19:09:36 +02:00
|
|
|
!(outerRseNeq = nodeAs<ComparativeBoolNode>(outerRse->rse_boolean)) ||
|
2010-09-17 05:15:32 +02:00
|
|
|
outerRseNeq->blrOp != blr_neq)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
RseNode* innerRse = static_cast<RseNode*>(outerRse->rse_relations[0].getObject()); // user rse
|
|
|
|
|
|
|
|
// If the rse is different than we expected, do nothing. Do nothing also if it uses FIRST or
|
|
|
|
// SKIP, as we can't inject booleans there without changing the behavior.
|
2020-06-06 21:36:08 +02:00
|
|
|
if (!innerRse || innerRse->getType() != RseNode::TYPE || innerRse->rse_first || innerRse->rse_skip)
|
2010-09-17 05:15:32 +02:00
|
|
|
return NULL;
|
|
|
|
|
2015-10-12 16:26:00 +02:00
|
|
|
NotBoolNode* newNode = FB_NEW_POOL(csb->csb_pool) NotBoolNode(csb->csb_pool);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2015-10-12 16:26:00 +02:00
|
|
|
BinaryBoolNode* orNode = FB_NEW_POOL(csb->csb_pool) BinaryBoolNode(csb->csb_pool, blr_or);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2010-09-20 18:07:50 +02:00
|
|
|
newNode->arg = orNode;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2015-10-12 16:26:00 +02:00
|
|
|
BinaryBoolNode* andNode = FB_NEW_POOL(csb->csb_pool) BinaryBoolNode(csb->csb_pool, blr_and);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2010-09-20 18:07:50 +02:00
|
|
|
orNode->arg1 = andNode;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2015-10-12 16:26:00 +02:00
|
|
|
MissingBoolNode* missNode = FB_NEW_POOL(csb->csb_pool) MissingBoolNode(csb->csb_pool);
|
2010-09-17 05:15:32 +02:00
|
|
|
missNode->arg = outerRseNeq->arg1;
|
|
|
|
|
2010-09-20 18:07:50 +02:00
|
|
|
andNode->arg1 = missNode;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2025-01-10 09:19:54 +01:00
|
|
|
RseNode* newInnerRse1 = innerRse->clone(csb->csb_pool);
|
|
|
|
newInnerRse1->flags |= RseNode::FLAG_SUB_QUERY;
|
|
|
|
|
2015-10-12 16:26:00 +02:00
|
|
|
RseBoolNode* rseBoolNode = FB_NEW_POOL(csb->csb_pool) RseBoolNode(csb->csb_pool, blr_any);
|
2025-01-10 09:19:54 +01:00
|
|
|
rseBoolNode->rse = newInnerRse1;
|
2014-03-19 23:31:50 +01:00
|
|
|
rseBoolNode->ownSavepoint = this->ownSavepoint;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2010-09-20 18:07:50 +02:00
|
|
|
andNode->arg2 = rseBoolNode;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2025-01-10 09:19:54 +01:00
|
|
|
RseNode* newInnerRse2 = innerRse->clone(csb->csb_pool);
|
|
|
|
newInnerRse2->flags |= RseNode::FLAG_SUB_QUERY;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2015-10-12 16:26:00 +02:00
|
|
|
rseBoolNode = FB_NEW_POOL(csb->csb_pool) RseBoolNode(csb->csb_pool, blr_any);
|
2025-01-10 09:19:54 +01:00
|
|
|
rseBoolNode->rse = newInnerRse2;
|
2014-03-19 23:31:50 +01:00
|
|
|
rseBoolNode->ownSavepoint = this->ownSavepoint;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2010-09-20 18:07:50 +02:00
|
|
|
orNode->arg2 = rseBoolNode;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2015-10-12 16:26:00 +02:00
|
|
|
BinaryBoolNode* boolean = FB_NEW_POOL(csb->csb_pool) BinaryBoolNode(csb->csb_pool, blr_or);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2015-10-12 16:26:00 +02:00
|
|
|
missNode = FB_NEW_POOL(csb->csb_pool) MissingBoolNode(csb->csb_pool);
|
2010-09-17 05:15:32 +02:00
|
|
|
missNode->arg = outerRseNeq->arg2;
|
|
|
|
|
2010-09-20 18:07:50 +02:00
|
|
|
boolean->arg1 = missNode;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2010-09-20 18:07:50 +02:00
|
|
|
boolean->arg2 = outerRse->rse_boolean;
|
2010-09-17 05:15:32 +02:00
|
|
|
outerRseNeq->blrOp = blr_eql;
|
|
|
|
|
|
|
|
// If there was a boolean on the stream, append (AND) the new one
|
2025-01-10 09:19:54 +01:00
|
|
|
if (newInnerRse2->rse_boolean)
|
2010-09-17 05:15:32 +02:00
|
|
|
{
|
2015-10-12 16:26:00 +02:00
|
|
|
andNode = FB_NEW_POOL(csb->csb_pool) BinaryBoolNode(csb->csb_pool, blr_and);
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2025-01-10 09:19:54 +01:00
|
|
|
andNode->arg1 = newInnerRse2->rse_boolean;
|
2010-09-17 05:15:32 +02:00
|
|
|
andNode->arg2 = boolean;
|
2010-09-20 18:07:50 +02:00
|
|
|
boolean = andNode;
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|
|
|
|
|
2025-01-10 09:19:54 +01:00
|
|
|
newInnerRse2->rse_boolean = boolean;
|
2010-09-17 05:15:32 +02:00
|
|
|
|
2017-09-27 17:55:08 +02:00
|
|
|
SubExprNodeCopier copier(csb->csb_pool, csb);
|
2010-11-21 04:47:29 +01:00
|
|
|
return copier.copy(tdbb, static_cast<BoolExprNode*>(newNode));
|
2010-09-17 05:15:32 +02:00
|
|
|
}
|