mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-22 14:43:03 +01:00
1) Refactored LOWER, UPPER and TRIM.
2) Fixed CORE-3174 - Expression index with TRIM may lead to incorrect indexed lookup
This commit is contained in:
parent
f76961d639
commit
ac3c00d503
@ -22,6 +22,7 @@
|
||||
#include <math.h>
|
||||
#include "../jrd/common.h"
|
||||
#include "../common/classes/FpeControl.h"
|
||||
#include "../common/classes/VaryStr.h"
|
||||
#include "../dsql/ExprNodes.h"
|
||||
#include "../dsql/node.h"
|
||||
#include "../jrd/align.h"
|
||||
@ -4309,6 +4310,205 @@ dsc* ParameterNode::execute(thread_db* tdbb, jrd_req* request) const
|
||||
//--------------------
|
||||
|
||||
|
||||
static RegisterNode<StrCaseNode> regStrCaseNodeLower(blr_lowcase);
|
||||
static RegisterNode<StrCaseNode> regStrCaseNodeUpper(blr_upcase);
|
||||
|
||||
StrCaseNode::StrCaseNode(MemoryPool& pool, UCHAR aBlrOp, dsql_nod* aArg)
|
||||
: TypedNode<ValueExprNode, ExprNode::TYPE_STR_CASE>(pool),
|
||||
blrOp(aBlrOp),
|
||||
dsqlArg(aArg),
|
||||
arg(NULL)
|
||||
{
|
||||
addChildNode(dsqlArg, arg);
|
||||
}
|
||||
|
||||
DmlNode* StrCaseNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, UCHAR blrOp)
|
||||
{
|
||||
StrCaseNode* node = FB_NEW(pool) StrCaseNode(pool, blrOp);
|
||||
node->arg = PAR_parse_node(tdbb, csb, VALUE);
|
||||
return node;
|
||||
}
|
||||
|
||||
void StrCaseNode::print(string& text, Array<dsql_nod*>& nodes) const
|
||||
{
|
||||
text.printf("StrCaseNode (%s)", (blrOp == blr_lowcase ? "lower" : "upper"));
|
||||
ExprNode::print(text, nodes);
|
||||
}
|
||||
|
||||
ValueExprNode* StrCaseNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
||||
{
|
||||
return FB_NEW(getPool()) StrCaseNode(getPool(), blrOp, PASS1_node(dsqlScratch, dsqlArg));
|
||||
}
|
||||
|
||||
void StrCaseNode::setParameterName(dsql_par* parameter) const
|
||||
{
|
||||
parameter->par_name = parameter->par_alias = (blrOp == blr_lowcase ? "LOWER" : "UPPER");
|
||||
}
|
||||
|
||||
bool StrCaseNode::setParameterType(DsqlCompilerScratch* dsqlScratch, dsql_nod* thisNode,
|
||||
dsql_nod* node, bool forceVarChar)
|
||||
{
|
||||
return PASS1_set_parameter_type(dsqlScratch, dsqlArg, node, forceVarChar);
|
||||
}
|
||||
|
||||
void StrCaseNode::genBlr(DsqlCompilerScratch* dsqlScratch)
|
||||
{
|
||||
dsqlScratch->appendUChar(blrOp);
|
||||
GEN_expr(dsqlScratch, dsqlArg);
|
||||
}
|
||||
|
||||
void StrCaseNode::make(DsqlCompilerScratch* dsqlScratch, dsql_nod* /*thisNode*/, dsc* desc,
|
||||
dsql_nod* nullReplacement)
|
||||
{
|
||||
dsc desc1;
|
||||
MAKE_desc(dsqlScratch, &desc1, dsqlArg, nullReplacement);
|
||||
|
||||
if (desc1.dsc_dtype <= dtype_any_text || desc1.dsc_dtype == dtype_blob)
|
||||
{
|
||||
*desc = desc1;
|
||||
return;
|
||||
}
|
||||
|
||||
desc->dsc_dtype = dtype_varying;
|
||||
desc->dsc_scale = 0;
|
||||
desc->dsc_ttype() = ttype_ascii;
|
||||
desc->dsc_length = sizeof(USHORT) + DSC_string_length(&desc1);
|
||||
desc->dsc_flags = desc1.dsc_flags & DSC_nullable;
|
||||
}
|
||||
|
||||
void StrCaseNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc)
|
||||
{
|
||||
CMP_get_desc(tdbb, csb, arg, desc);
|
||||
|
||||
if (desc->dsc_dtype > dtype_varying && desc->dsc_dtype != dtype_blob)
|
||||
{
|
||||
desc->dsc_length = DSC_convert_to_text_length(desc->dsc_dtype);
|
||||
desc->dsc_dtype = dtype_text;
|
||||
desc->dsc_ttype() = ttype_ascii;
|
||||
desc->dsc_scale = 0;
|
||||
desc->dsc_flags = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ValueExprNode* StrCaseNode::copy(thread_db* tdbb, NodeCopier& copier)
|
||||
{
|
||||
StrCaseNode* node = FB_NEW(*tdbb->getDefaultPool()) StrCaseNode(*tdbb->getDefaultPool(), blrOp);
|
||||
node->arg = copier.copy(tdbb, arg);
|
||||
return node;
|
||||
}
|
||||
|
||||
bool StrCaseNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const
|
||||
{
|
||||
if (!ExprNode::dsqlMatch(other, ignoreMapCast))
|
||||
return false;
|
||||
|
||||
const StrCaseNode* o = other->as<StrCaseNode>();
|
||||
fb_assert(o)
|
||||
|
||||
return blrOp == o->blrOp;
|
||||
}
|
||||
|
||||
bool StrCaseNode::expressionEqual(thread_db* tdbb, CompilerScratch* csb, /*const*/ ExprNode* other,
|
||||
USHORT stream)
|
||||
{
|
||||
if (!ExprNode::expressionEqual(tdbb, csb, other, stream))
|
||||
return false;
|
||||
|
||||
StrCaseNode* otherNode = other->as<StrCaseNode>();
|
||||
fb_assert(otherNode);
|
||||
|
||||
return blrOp == otherNode->blrOp;
|
||||
}
|
||||
|
||||
ExprNode* StrCaseNode::pass2(thread_db* tdbb, CompilerScratch* csb)
|
||||
{
|
||||
ExprNode::pass2(tdbb, csb);
|
||||
|
||||
dsc desc;
|
||||
getDesc(tdbb, csb, &desc);
|
||||
node->nod_impure = CMP_impure(csb, sizeof(impure_value));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// Low/up case a string.
|
||||
dsc* StrCaseNode::execute(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
impure_value* const impure = request->getImpure<impure_value>(node->nod_impure);
|
||||
request->req_flags &= ~req_null;
|
||||
|
||||
const dsc* value = EVL_expr(tdbb, arg);
|
||||
|
||||
if (request->req_flags & req_null)
|
||||
return NULL;
|
||||
|
||||
TextType* textType = INTL_texttype_lookup(tdbb, value->getTextType());
|
||||
ULONG (TextType::*intlFunction)(ULONG, const UCHAR*, ULONG, UCHAR*) =
|
||||
(blrOp == blr_lowcase ? &TextType::str_to_lower : &TextType::str_to_upper);
|
||||
|
||||
if (value->isBlob())
|
||||
{
|
||||
EVL_make_value(tdbb, value, impure);
|
||||
|
||||
if (value->dsc_sub_type != isc_blob_text)
|
||||
return &impure->vlu_desc;
|
||||
|
||||
CharSet* charSet = textType->getCharSet();
|
||||
|
||||
blb* blob = BLB_open(tdbb, tdbb->getRequest()->req_transaction,
|
||||
reinterpret_cast<bid*>(value->dsc_address));
|
||||
|
||||
HalfStaticArray<UCHAR, BUFFER_SMALL> buffer;
|
||||
|
||||
if (charSet->isMultiByte())
|
||||
buffer.getBuffer(blob->blb_length); // alloc space to put entire blob in memory
|
||||
|
||||
blb* newBlob = BLB_create(tdbb, tdbb->getRequest()->req_transaction,
|
||||
&impure->vlu_misc.vlu_bid);
|
||||
|
||||
while (!(blob->blb_flags & BLB_eof))
|
||||
{
|
||||
SLONG len = BLB_get_data(tdbb, blob, buffer.begin(), buffer.getCapacity(), false);
|
||||
|
||||
if (len)
|
||||
{
|
||||
len = (textType->*intlFunction)(len, buffer.begin(), len, buffer.begin());
|
||||
BLB_put_data(tdbb, newBlob, buffer.begin(), len);
|
||||
}
|
||||
}
|
||||
|
||||
BLB_close(tdbb, newBlob);
|
||||
BLB_close(tdbb, blob);
|
||||
}
|
||||
else
|
||||
{
|
||||
UCHAR* ptr;
|
||||
VaryStr<32> temp;
|
||||
USHORT ttype;
|
||||
|
||||
dsc desc;
|
||||
desc.dsc_length = MOV_get_string_ptr(value, &ttype, &ptr, &temp, sizeof(temp));
|
||||
desc.dsc_dtype = dtype_text;
|
||||
desc.dsc_address = NULL;
|
||||
desc.setTextType(ttype);
|
||||
EVL_make_value(tdbb, &desc, impure);
|
||||
|
||||
ULONG len = (textType->*intlFunction)(desc.dsc_length,
|
||||
ptr, desc.dsc_length, impure->vlu_desc.dsc_address);
|
||||
|
||||
if (len == INTL_BAD_STR_LENGTH)
|
||||
status_exception::raise(Arg::Gds(isc_arith_except));
|
||||
|
||||
impure->vlu_desc.dsc_length = (USHORT) len;
|
||||
}
|
||||
|
||||
return &impure->vlu_desc;
|
||||
}
|
||||
|
||||
|
||||
//--------------------
|
||||
|
||||
|
||||
static RegisterNode<SubstringSimilarNode> regSubstringSimilarNode(blr_substring_similar);
|
||||
|
||||
SubstringSimilarNode::SubstringSimilarNode(MemoryPool& pool, dsql_nod* aExpr, dsql_nod* aPattern,
|
||||
@ -4739,6 +4939,315 @@ ValueExprNode* SysFuncCallNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
||||
//--------------------
|
||||
|
||||
|
||||
static RegisterNode<TrimNode> regTrimNode(blr_trim);
|
||||
|
||||
TrimNode::TrimNode(MemoryPool& pool, UCHAR aWhere, dsql_nod* aValue, dsql_nod* aTrimChars)
|
||||
: TypedNode<ValueExprNode, ExprNode::TYPE_TRIM>(pool),
|
||||
where(aWhere),
|
||||
dsqlValue(aValue),
|
||||
dsqlTrimChars(aTrimChars),
|
||||
value(NULL),
|
||||
trimChars(NULL)
|
||||
{
|
||||
addChildNode(dsqlValue, value);
|
||||
addChildNode(dsqlTrimChars, trimChars);
|
||||
}
|
||||
|
||||
DmlNode* TrimNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, UCHAR /*blrOp*/)
|
||||
{
|
||||
UCHAR where = csb->csb_blr_reader.getByte();
|
||||
UCHAR what = csb->csb_blr_reader.getByte();
|
||||
|
||||
TrimNode* node = FB_NEW(pool) TrimNode(pool, where);
|
||||
|
||||
if (what == blr_trim_characters)
|
||||
node->trimChars = PAR_parse_node(tdbb, csb, VALUE);
|
||||
|
||||
node->value = PAR_parse_node(tdbb, csb, VALUE);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
void TrimNode::print(string& text, Array<dsql_nod*>& nodes) const
|
||||
{
|
||||
text = "TrimNode";
|
||||
ExprNode::print(text, nodes);
|
||||
}
|
||||
|
||||
ValueExprNode* TrimNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
||||
{
|
||||
TrimNode* node = FB_NEW(getPool()) TrimNode(getPool(), where,
|
||||
PASS1_node(dsqlScratch, dsqlValue), PASS1_node(dsqlScratch, dsqlTrimChars));
|
||||
|
||||
// Try to force trimChars to be same type as value: TRIM(? FROM FIELD)
|
||||
PASS1_set_parameter_type(dsqlScratch, node->dsqlTrimChars, node->dsqlValue, false);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
void TrimNode::setParameterName(dsql_par* parameter) const
|
||||
{
|
||||
parameter->par_name = parameter->par_alias = "TRIM";
|
||||
}
|
||||
|
||||
bool TrimNode::setParameterType(DsqlCompilerScratch* dsqlScratch, dsql_nod* thisNode,
|
||||
dsql_nod* node, bool forceVarChar)
|
||||
{
|
||||
return PASS1_set_parameter_type(dsqlScratch, dsqlValue, node, forceVarChar) |
|
||||
PASS1_set_parameter_type(dsqlScratch, dsqlTrimChars, node, forceVarChar);
|
||||
}
|
||||
|
||||
void TrimNode::genBlr(DsqlCompilerScratch* dsqlScratch)
|
||||
{
|
||||
dsqlScratch->appendUChar(blr_trim);
|
||||
dsqlScratch->appendUChar(where);
|
||||
|
||||
if (dsqlTrimChars)
|
||||
{
|
||||
dsqlScratch->appendUChar(blr_trim_characters);
|
||||
GEN_expr(dsqlScratch, dsqlTrimChars);
|
||||
}
|
||||
else
|
||||
dsqlScratch->appendUChar(blr_trim_spaces);
|
||||
|
||||
GEN_expr(dsqlScratch, dsqlValue);
|
||||
}
|
||||
|
||||
void TrimNode::make(DsqlCompilerScratch* dsqlScratch, dsql_nod* /*thisNode*/, dsc* desc,
|
||||
dsql_nod* nullReplacement)
|
||||
{
|
||||
dsc desc1, desc2;
|
||||
|
||||
MAKE_desc(dsqlScratch, &desc1, dsqlValue, nullReplacement);
|
||||
|
||||
if (dsqlTrimChars)
|
||||
MAKE_desc(dsqlScratch, &desc2, dsqlTrimChars, nullReplacement);
|
||||
else
|
||||
desc2.dsc_flags = 0;
|
||||
|
||||
if (desc1.dsc_dtype == dtype_blob)
|
||||
{
|
||||
*desc = desc1;
|
||||
desc->dsc_flags |= (desc1.dsc_flags | desc2.dsc_flags) & DSC_nullable;
|
||||
}
|
||||
else if (desc1.dsc_dtype <= dtype_any_text)
|
||||
{
|
||||
*desc = desc1;
|
||||
desc->dsc_dtype = dtype_varying;
|
||||
desc->dsc_length = sizeof(USHORT) + DSC_string_length(&desc1);
|
||||
desc->dsc_flags = (desc1.dsc_flags | desc2.dsc_flags) & DSC_nullable;
|
||||
}
|
||||
else
|
||||
{
|
||||
desc->dsc_dtype = dtype_varying;
|
||||
desc->dsc_scale = 0;
|
||||
desc->dsc_ttype() = ttype_ascii;
|
||||
desc->dsc_length = sizeof(USHORT) + DSC_string_length(&desc1);
|
||||
desc->dsc_flags = (desc1.dsc_flags | desc2.dsc_flags) & DSC_nullable;
|
||||
}
|
||||
}
|
||||
|
||||
void TrimNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc)
|
||||
{
|
||||
CMP_get_desc(tdbb, csb, value, desc);
|
||||
|
||||
if (trimChars)
|
||||
{
|
||||
dsc desc1;
|
||||
CMP_get_desc(tdbb, csb, trimChars, &desc1);
|
||||
desc->dsc_flags |= desc1.dsc_flags & DSC_null;
|
||||
}
|
||||
|
||||
if (desc->dsc_dtype != dtype_blob)
|
||||
{
|
||||
USHORT length = DSC_string_length(desc);
|
||||
|
||||
if (!DTYPE_IS_TEXT(desc->dsc_dtype))
|
||||
{
|
||||
desc->dsc_ttype() = ttype_ascii;
|
||||
desc->dsc_scale = 0;
|
||||
}
|
||||
|
||||
desc->dsc_dtype = dtype_varying;
|
||||
desc->dsc_length = length + sizeof(USHORT);
|
||||
}
|
||||
}
|
||||
|
||||
ValueExprNode* TrimNode::copy(thread_db* tdbb, NodeCopier& copier)
|
||||
{
|
||||
TrimNode* node = FB_NEW(*tdbb->getDefaultPool()) TrimNode(*tdbb->getDefaultPool(), where);
|
||||
node->value = copier.copy(tdbb, value);
|
||||
node->trimChars = copier.copy(tdbb, trimChars);
|
||||
return node;
|
||||
}
|
||||
|
||||
bool TrimNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const
|
||||
{
|
||||
if (!ExprNode::dsqlMatch(other, ignoreMapCast))
|
||||
return false;
|
||||
|
||||
const TrimNode* o = other->as<TrimNode>();
|
||||
fb_assert(o)
|
||||
|
||||
return where == o->where;
|
||||
}
|
||||
|
||||
bool TrimNode::expressionEqual(thread_db* tdbb, CompilerScratch* csb, /*const*/ ExprNode* other,
|
||||
USHORT stream)
|
||||
{
|
||||
if (!ExprNode::expressionEqual(tdbb, csb, other, stream))
|
||||
return false;
|
||||
|
||||
TrimNode* o = other->as<TrimNode>();
|
||||
fb_assert(o);
|
||||
|
||||
return where == o->where;
|
||||
}
|
||||
|
||||
ExprNode* TrimNode::pass2(thread_db* tdbb, CompilerScratch* csb)
|
||||
{
|
||||
ExprNode::pass2(tdbb, csb);
|
||||
|
||||
dsc desc;
|
||||
getDesc(tdbb, csb, &desc);
|
||||
node->nod_impure = CMP_impure(csb, sizeof(impure_value));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// Perform trim function = TRIM([where what FROM] string).
|
||||
dsc* TrimNode::execute(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
impure_value* impure = request->getImpure<impure_value>(node->nod_impure);
|
||||
request->req_flags &= ~req_null;
|
||||
|
||||
dsc* trimCharsDesc = (trimChars ? EVL_expr(tdbb, trimChars) : NULL);
|
||||
if (request->req_flags & req_null)
|
||||
return NULL;
|
||||
|
||||
dsc* valueDesc = EVL_expr(tdbb, value);
|
||||
if (request->req_flags & req_null)
|
||||
return NULL;
|
||||
|
||||
USHORT ttype = INTL_TEXT_TYPE(*valueDesc);
|
||||
TextType* tt = INTL_texttype_lookup(tdbb, ttype);
|
||||
CharSet* cs = tt->getCharSet();
|
||||
|
||||
const UCHAR* charactersAddress;
|
||||
MoveBuffer charactersBuffer;
|
||||
USHORT charactersLength;
|
||||
|
||||
if (trimCharsDesc)
|
||||
{
|
||||
UCHAR* tempAddress = 0;
|
||||
charactersLength = MOV_make_string2(tdbb, trimCharsDesc, ttype, &tempAddress, charactersBuffer);
|
||||
charactersAddress = tempAddress;
|
||||
}
|
||||
else
|
||||
{
|
||||
charactersLength = tt->getCharSet()->getSpaceLength();
|
||||
charactersAddress = tt->getCharSet()->getSpace();
|
||||
}
|
||||
|
||||
HalfStaticArray<UCHAR, BUFFER_SMALL> charactersCanonical;
|
||||
charactersCanonical.getBuffer(charactersLength / tt->getCharSet()->minBytesPerChar() *
|
||||
tt->getCanonicalWidth());
|
||||
const SLONG charactersCanonicalLen = tt->canonical(charactersLength, charactersAddress,
|
||||
charactersCanonical.getCount(), charactersCanonical.begin()) * tt->getCanonicalWidth();
|
||||
|
||||
HalfStaticArray<UCHAR, BUFFER_SMALL> blobBuffer;
|
||||
MoveBuffer valueBuffer;
|
||||
UCHAR* valueAddress;
|
||||
ULONG valueLength;
|
||||
|
||||
if (valueDesc->isBlob())
|
||||
{
|
||||
// Source string is a blob, things get interesting.
|
||||
blb* blob = BLB_open(tdbb, tdbb->getRequest()->req_transaction,
|
||||
reinterpret_cast<bid*>(valueDesc->dsc_address));
|
||||
|
||||
// It's very difficult (and probably not very efficient) to trim a blob in chunks.
|
||||
// So go simple way and always read entire blob in memory.
|
||||
valueAddress = blobBuffer.getBuffer(blob->blb_length);
|
||||
valueLength = BLB_get_data(tdbb, blob, valueAddress, blob->blb_length, true);
|
||||
}
|
||||
else
|
||||
valueLength = MOV_make_string2(tdbb, valueDesc, ttype, &valueAddress, valueBuffer);
|
||||
|
||||
HalfStaticArray<UCHAR, BUFFER_SMALL> valueCanonical;
|
||||
valueCanonical.getBuffer(valueLength / cs->minBytesPerChar() * tt->getCanonicalWidth());
|
||||
const SLONG valueCanonicalLen = tt->canonical(valueLength, valueAddress,
|
||||
valueCanonical.getCount(), valueCanonical.begin()) * tt->getCanonicalWidth();
|
||||
|
||||
SLONG offsetLead = 0;
|
||||
SLONG offsetTrail = valueCanonicalLen;
|
||||
|
||||
// CVC: Avoid endless loop with zero length trim chars.
|
||||
if (charactersCanonicalLen)
|
||||
{
|
||||
if (where == blr_trim_both || where == blr_trim_leading)
|
||||
{
|
||||
// CVC: Prevent surprises with offsetLead < valueCanonicalLen; it may fail.
|
||||
for (; offsetLead + charactersCanonicalLen <= valueCanonicalLen;
|
||||
offsetLead += charactersCanonicalLen)
|
||||
{
|
||||
if (memcmp(charactersCanonical.begin(), &valueCanonical[offsetLead],
|
||||
charactersCanonicalLen) != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (where == blr_trim_both || where == blr_trim_trailing)
|
||||
{
|
||||
for (; offsetTrail - charactersCanonicalLen >= offsetLead;
|
||||
offsetTrail -= charactersCanonicalLen)
|
||||
{
|
||||
if (memcmp(charactersCanonical.begin(),
|
||||
&valueCanonical[offsetTrail - charactersCanonicalLen],
|
||||
charactersCanonicalLen) != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (valueDesc->isBlob())
|
||||
{
|
||||
// We have valueCanonical already allocated.
|
||||
// Use it to get the substring that will be written to the new blob.
|
||||
const ULONG len = cs->substring(valueLength, valueAddress,
|
||||
valueCanonical.getCapacity(), valueCanonical.begin(),
|
||||
offsetLead / tt->getCanonicalWidth(),
|
||||
(offsetTrail - offsetLead) / tt->getCanonicalWidth());
|
||||
|
||||
EVL_make_value(tdbb, valueDesc, impure);
|
||||
|
||||
blb* newBlob = BLB_create(tdbb, tdbb->getRequest()->req_transaction, &impure->vlu_misc.vlu_bid);
|
||||
BLB_put_data(tdbb, newBlob, valueCanonical.begin(), len);
|
||||
BLB_close(tdbb, newBlob);
|
||||
}
|
||||
else
|
||||
{
|
||||
dsc desc;
|
||||
desc.makeText(valueLength, ttype);
|
||||
EVL_make_value(tdbb, &desc, impure);
|
||||
|
||||
impure->vlu_desc.dsc_length = cs->substring(valueLength, valueAddress,
|
||||
impure->vlu_desc.dsc_length, impure->vlu_desc.dsc_address,
|
||||
offsetLead / tt->getCanonicalWidth(),
|
||||
(offsetTrail - offsetLead) / tt->getCanonicalWidth());
|
||||
}
|
||||
|
||||
return &impure->vlu_desc;
|
||||
}
|
||||
|
||||
|
||||
//--------------------
|
||||
|
||||
|
||||
static RegisterNode<UdfCallNode> regUdfCallNode1(blr_function);
|
||||
static RegisterNode<UdfCallNode> regUdfCallNode2(blr_function2);
|
||||
|
||||
|
@ -449,6 +449,37 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class StrCaseNode : public TypedNode<ValueExprNode, ExprNode::TYPE_STR_CASE>
|
||||
{
|
||||
public:
|
||||
StrCaseNode(MemoryPool& pool, UCHAR aBlrOp, dsql_nod* aArg = NULL);
|
||||
|
||||
static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, UCHAR blrOp);
|
||||
|
||||
virtual void print(Firebird::string& text, Firebird::Array<dsql_nod*>& nodes) const;
|
||||
virtual ValueExprNode* dsqlPass(DsqlCompilerScratch* dsqlScratch);
|
||||
virtual void setParameterName(dsql_par* parameter) const;
|
||||
virtual bool setParameterType(DsqlCompilerScratch* dsqlScratch, dsql_nod* thisNode,
|
||||
dsql_nod* node, bool forceVarChar);
|
||||
virtual void genBlr(DsqlCompilerScratch* dsqlScratch);
|
||||
virtual void make(DsqlCompilerScratch* dsqlScratch, dsql_nod* thisNode, dsc* desc,
|
||||
dsql_nod* nullReplacement);
|
||||
|
||||
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
|
||||
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier);
|
||||
virtual bool dsqlMatch(const ExprNode* other, bool ignoreMapCast) const;
|
||||
virtual bool expressionEqual(thread_db* tdbb, CompilerScratch* csb, /*const*/ ExprNode* other,
|
||||
USHORT stream) /*const*/;
|
||||
virtual ExprNode* pass2(thread_db* tdbb, CompilerScratch* csb);
|
||||
virtual dsc* execute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
public:
|
||||
UCHAR blrOp;
|
||||
dsql_nod* dsqlArg;
|
||||
NestConst<jrd_nod> arg;
|
||||
};
|
||||
|
||||
|
||||
class SubstringSimilarNode : public TypedNode<ValueExprNode, ExprNode::TYPE_SUBSTRING_SIMILAR>
|
||||
{
|
||||
public:
|
||||
@ -514,6 +545,40 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class TrimNode : public TypedNode<ValueExprNode, ExprNode::TYPE_TRIM>
|
||||
{
|
||||
public:
|
||||
explicit TrimNode(MemoryPool& pool, UCHAR aWhere,
|
||||
dsql_nod* aValue = NULL, dsql_nod* aTrimChars = NULL);
|
||||
|
||||
static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, UCHAR blrOp);
|
||||
|
||||
virtual void print(Firebird::string& text, Firebird::Array<dsql_nod*>& nodes) const;
|
||||
virtual ValueExprNode* dsqlPass(DsqlCompilerScratch* dsqlScratch);
|
||||
virtual void setParameterName(dsql_par* parameter) const;
|
||||
virtual bool setParameterType(DsqlCompilerScratch* dsqlScratch, dsql_nod* thisNode,
|
||||
dsql_nod* node, bool forceVarChar);
|
||||
virtual void genBlr(DsqlCompilerScratch* dsqlScratch);
|
||||
virtual void make(DsqlCompilerScratch* dsqlScratch, dsql_nod* thisNode, dsc* desc,
|
||||
dsql_nod* nullReplacement);
|
||||
|
||||
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
|
||||
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier);
|
||||
virtual bool dsqlMatch(const ExprNode* other, bool ignoreMapCast) const;
|
||||
virtual bool expressionEqual(thread_db* tdbb, CompilerScratch* csb, /*const*/ ExprNode* other,
|
||||
USHORT stream) /*const*/;
|
||||
virtual ExprNode* pass2(thread_db* tdbb, CompilerScratch* csb);
|
||||
virtual dsc* execute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
public:
|
||||
UCHAR where;
|
||||
dsql_nod* dsqlValue;
|
||||
dsql_nod* dsqlTrimChars; // may be NULL
|
||||
NestConst<jrd_nod> value;
|
||||
NestConst<jrd_nod> trimChars; // may be NULL
|
||||
};
|
||||
|
||||
|
||||
class UdfCallNode : public TypedNode<ValueExprNode, ExprNode::TYPE_UDF_CALL>
|
||||
{
|
||||
public:
|
||||
|
@ -272,8 +272,10 @@ public:
|
||||
TYPE_OVER,
|
||||
TYPE_PARAMETER,
|
||||
TYPE_RSE_BOOL,
|
||||
TYPE_STR_CASE,
|
||||
TYPE_SUBSTRING_SIMILAR,
|
||||
TYPE_SYSFUNC_CALL,
|
||||
TYPE_TRIM,
|
||||
TYPE_UDF_CALL,
|
||||
TYPE_VALUE_IF
|
||||
};
|
||||
|
@ -520,9 +520,6 @@ inline bool DsqlNodeVisitor<T, T2>::visitChildren(T node)
|
||||
break;
|
||||
|
||||
case nod_substr:
|
||||
case nod_trim:
|
||||
case nod_upcase:
|
||||
case nod_lowcase:
|
||||
case nod_extract:
|
||||
case nod_strlen:
|
||||
case nod_simple_case:
|
||||
|
@ -311,12 +311,6 @@ void GEN_expr(DsqlCompilerScratch* dsqlScratch, dsql_nod* node)
|
||||
blr_operator = blr_via;
|
||||
break;
|
||||
|
||||
case nod_upcase:
|
||||
blr_operator = blr_upcase;
|
||||
break;
|
||||
case nod_lowcase:
|
||||
blr_operator = blr_lowcase;
|
||||
break;
|
||||
case nod_substr:
|
||||
blr_operator = blr_substring;
|
||||
break;
|
||||
@ -333,21 +327,6 @@ void GEN_expr(DsqlCompilerScratch* dsqlScratch, dsql_nod* node)
|
||||
gen_searched_case(dsqlScratch, node);
|
||||
return;
|
||||
|
||||
case nod_trim:
|
||||
dsqlScratch->appendUChar(blr_trim);
|
||||
dsqlScratch->appendUChar(node->nod_arg[e_trim_specification]->getSlong());
|
||||
|
||||
if (node->nod_arg[e_trim_characters])
|
||||
{
|
||||
dsqlScratch->appendUChar(blr_trim_characters);
|
||||
GEN_expr(dsqlScratch, node->nod_arg[e_trim_characters]);
|
||||
}
|
||||
else
|
||||
dsqlScratch->appendUChar(blr_trim_spaces);
|
||||
|
||||
GEN_expr(dsqlScratch, node->nod_arg[e_trim_value]);
|
||||
return;
|
||||
|
||||
case nod_assign:
|
||||
dsqlScratch->appendUChar(blr_assignment);
|
||||
GEN_expr(dsqlScratch, node->nod_arg[0]);
|
||||
|
@ -426,22 +426,6 @@ void MAKE_desc(DsqlCompilerScratch* dsqlScratch, dsc* desc, dsql_nod* node, dsql
|
||||
MAKE_desc(dsqlScratch, desc, node->nod_arg[e_derived_field_value], null_replacement);
|
||||
return;
|
||||
|
||||
case nod_upcase:
|
||||
case nod_lowcase:
|
||||
MAKE_desc(dsqlScratch, &desc1, node->nod_arg[0], null_replacement);
|
||||
if (desc1.dsc_dtype <= dtype_any_text || desc1.dsc_dtype == dtype_blob)
|
||||
{
|
||||
*desc = desc1;
|
||||
return;
|
||||
}
|
||||
|
||||
desc->dsc_dtype = dtype_varying;
|
||||
desc->dsc_scale = 0;
|
||||
desc->dsc_ttype() = ttype_ascii;
|
||||
desc->dsc_length = sizeof(USHORT) + DSC_string_length(&desc1);
|
||||
desc->dsc_flags = desc1.dsc_flags & DSC_nullable;
|
||||
return;
|
||||
|
||||
case nod_substr:
|
||||
MAKE_desc(dsqlScratch, &desc1, node->nod_arg[0], null_replacement);
|
||||
MAKE_desc(dsqlScratch, &desc2, node->nod_arg[1], null_replacement);
|
||||
@ -449,36 +433,6 @@ void MAKE_desc(DsqlCompilerScratch* dsqlScratch, dsc* desc, dsql_nod* node, dsql
|
||||
DSqlDataTypeUtil(dsqlScratch).makeSubstr(desc, &desc1, &desc2, &desc3);
|
||||
return;
|
||||
|
||||
case nod_trim:
|
||||
MAKE_desc(dsqlScratch, &desc1, node->nod_arg[e_trim_value], null_replacement);
|
||||
if (node->nod_arg[e_trim_characters])
|
||||
MAKE_desc(dsqlScratch, &desc2, node->nod_arg[e_trim_characters], null_replacement);
|
||||
else
|
||||
desc2.dsc_flags = 0;
|
||||
|
||||
if (desc1.dsc_dtype == dtype_blob)
|
||||
{
|
||||
*desc = desc1;
|
||||
desc->dsc_flags |= (desc1.dsc_flags | desc2.dsc_flags) & DSC_nullable;
|
||||
}
|
||||
else if (desc1.dsc_dtype <= dtype_any_text)
|
||||
{
|
||||
*desc = desc1;
|
||||
desc->dsc_dtype = dtype_varying;
|
||||
desc->dsc_length = sizeof(USHORT) + DSC_string_length(&desc1);
|
||||
desc->dsc_flags = (desc1.dsc_flags | desc2.dsc_flags) & DSC_nullable;
|
||||
}
|
||||
else
|
||||
{
|
||||
desc->dsc_dtype = dtype_varying;
|
||||
desc->dsc_scale = 0;
|
||||
desc->dsc_ttype() = ttype_ascii;
|
||||
desc->dsc_length = sizeof(USHORT) + DSC_string_length(&desc1);
|
||||
desc->dsc_flags = (desc1.dsc_flags | desc2.dsc_flags) & DSC_nullable;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
case nod_cast:
|
||||
field = (dsql_fld*) node->nod_arg[e_cast_target];
|
||||
MAKE_desc_from_field(desc, field);
|
||||
@ -1226,18 +1180,9 @@ void MAKE_parameter_names(dsql_par* parameter, const dsql_nod* item)
|
||||
case nod_substr:
|
||||
name_alias = "SUBSTRING";
|
||||
break;
|
||||
case nod_trim:
|
||||
name_alias = "TRIM";
|
||||
break;
|
||||
case nod_cast:
|
||||
name_alias = "CAST";
|
||||
break;
|
||||
case nod_upcase:
|
||||
name_alias = "UPPER";
|
||||
break;
|
||||
case nod_lowcase:
|
||||
name_alias = "LOWER";
|
||||
break;
|
||||
case nod_extract:
|
||||
name_alias = "EXTRACT";
|
||||
break;
|
||||
|
@ -138,8 +138,6 @@ enum nod_t
|
||||
nod_null,
|
||||
nod_dbkey,
|
||||
nod_cast,
|
||||
nod_upcase,
|
||||
nod_lowcase,
|
||||
nod_collate,
|
||||
nod_get_segment, // blobs
|
||||
nod_put_segment,
|
||||
@ -212,7 +210,6 @@ enum nod_t
|
||||
nod_query_spec,
|
||||
nod_mod_udf,
|
||||
nod_strlen,
|
||||
nod_trim,
|
||||
nod_returning,
|
||||
nod_tra_misc,
|
||||
nod_lock_timeout,
|
||||
|
@ -799,7 +799,6 @@ inline void check_copy_incr(char*& to, const char ch, const char* const string)
|
||||
%type <legacyNode> table_proc table_proc_inputs table_reference table_subquery tbl_reserve_options
|
||||
%type <legacyNode> timestamp_part top tra_misc_options tra_timeout tran_opt tran_opt_list tran_opt_list_m
|
||||
%type <legacyNode> trim_function
|
||||
%type <legacyNode> trim_specification
|
||||
%type <uintVal> time_precision_opt timestamp_precision_opt
|
||||
|
||||
%type <legacyNode> u_constant u_numeric_constant udf udf_data_type udf_decl_clause undo_savepoint
|
||||
@ -869,6 +868,8 @@ inline void check_copy_incr(char*& to, const char ch, const char* const string)
|
||||
|
||||
%type <sysFuncCallNode> system_function_special_syntax
|
||||
|
||||
%type <blrOp> trim_specification
|
||||
|
||||
// Predicates
|
||||
%type <boolExprNode> between_predicate comparison_predicate distinct_predicate
|
||||
%type <boolExprNode> exists_predicate in_predicate binary_pattern_predicate ternary_pattern_predicate null_predicate predicate
|
||||
@ -5683,13 +5684,14 @@ system_function_special_syntax
|
||||
{ $$ = FB_NEW(getPool()) SysFuncCallNode(getPool(), toName($1), $3); }
|
||||
;
|
||||
|
||||
string_value_function : substring_function
|
||||
| trim_function
|
||||
| KW_UPPER '(' value ')'
|
||||
{ $$ = make_node (nod_upcase, 1, $3); }
|
||||
| KW_LOWER '(' value ')'
|
||||
{ $$ = make_node (nod_lowcase, 1, $3); }
|
||||
;
|
||||
string_value_function
|
||||
: substring_function
|
||||
| trim_function
|
||||
| KW_UPPER '(' value ')'
|
||||
{ $$ = makeClassNode(FB_NEW(getPool()) StrCaseNode(getPool(), blr_upcase, $3)); }
|
||||
| KW_LOWER '(' value ')'
|
||||
{ $$ = makeClassNode(FB_NEW(getPool()) StrCaseNode(getPool(), blr_lowcase, $3)); }
|
||||
;
|
||||
|
||||
substring_function
|
||||
: SUBSTRING '(' value FROM value string_length_opt ')'
|
||||
@ -5706,31 +5708,27 @@ substring_function
|
||||
{ $$ = makeClassNode(FB_NEW(getPool()) SubstringSimilarNode(getPool(), $3, $5, $7)); }
|
||||
;
|
||||
|
||||
string_length_opt : FOR value
|
||||
{ $$ = $2; }
|
||||
|
|
||||
{ $$ = MAKE_const_slong (SHRT_POS_MAX); }
|
||||
;
|
||||
string_length_opt
|
||||
: { $$ = MAKE_const_slong(SHRT_POS_MAX); }
|
||||
| FOR value { $$ = $2; }
|
||||
;
|
||||
|
||||
trim_function : TRIM '(' trim_specification value FROM value ')'
|
||||
{ $$ = make_node (nod_trim, (int) e_trim_count, $3, $4, $6); }
|
||||
| TRIM '(' value FROM value ')'
|
||||
{ $$ = make_node (nod_trim, (int) e_trim_count,
|
||||
MAKE_const_slong (blr_trim_both), $3, $5); }
|
||||
| TRIM '(' trim_specification FROM value ')'
|
||||
{ $$ = make_node (nod_trim, (int) e_trim_count, $3, NULL, $5); }
|
||||
| TRIM '(' value ')'
|
||||
{ $$ = make_node (nod_trim, (int) e_trim_count,
|
||||
MAKE_const_slong (blr_trim_both), NULL, $3); }
|
||||
;
|
||||
trim_function
|
||||
: TRIM '(' trim_specification value FROM value ')'
|
||||
{ $$ = makeClassNode(FB_NEW(getPool()) TrimNode(getPool(), $3, $6, $4)); }
|
||||
| TRIM '(' value FROM value ')'
|
||||
{ $$ = makeClassNode(FB_NEW(getPool()) TrimNode(getPool(), blr_trim_both, $5, $3)); }
|
||||
| TRIM '(' trim_specification FROM value ')'
|
||||
{ $$ = makeClassNode(FB_NEW(getPool()) TrimNode(getPool(), $3, $5, NULL)); }
|
||||
| TRIM '(' value ')'
|
||||
{ $$ = makeClassNode(FB_NEW(getPool()) TrimNode(getPool(), blr_trim_both, $3, NULL)); }
|
||||
;
|
||||
|
||||
trim_specification : BOTH
|
||||
{ $$ = MAKE_const_slong (blr_trim_both); }
|
||||
| TRAILING
|
||||
{ $$ = MAKE_const_slong (blr_trim_trailing); }
|
||||
| LEADING
|
||||
{ $$ = MAKE_const_slong (blr_trim_leading); }
|
||||
;
|
||||
trim_specification
|
||||
: BOTH { $$ = blr_trim_both; }
|
||||
| TRAILING { $$ = blr_trim_trailing; }
|
||||
| LEADING { $$ = blr_trim_leading; }
|
||||
;
|
||||
|
||||
udf
|
||||
: symbol_UDF_call_name '(' value_list ')'
|
||||
|
@ -1575,14 +1575,6 @@ dsql_nod* PASS1_node(DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
|
||||
|
||||
break;
|
||||
|
||||
case nod_trim:
|
||||
sub1 = node->nod_arg[e_trim_characters];
|
||||
sub2 = node->nod_arg[e_trim_value];
|
||||
|
||||
// Try to force sub1 to be same type as sub2 eg: TRIM(? FROM FIELD) case
|
||||
PASS1_set_parameter_type(dsqlScratch, sub1, sub2, false);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -8532,9 +8524,6 @@ bool PASS1_set_parameter_type(DsqlCompilerScratch* dsqlScratch, dsql_nod* in_nod
|
||||
}
|
||||
|
||||
case nod_substr:
|
||||
case nod_trim:
|
||||
case nod_upcase:
|
||||
case nod_lowcase:
|
||||
case nod_extract:
|
||||
case nod_limit:
|
||||
case nod_rows:
|
||||
@ -8636,7 +8625,9 @@ static void set_parameter_name( dsql_nod* par_node, const dsql_nod* fld_node, co
|
||||
case ExprNode::TYPE_ARITHMETIC:
|
||||
case ExprNode::TYPE_CONCATENATE:
|
||||
case ExprNode::TYPE_NEGATE:
|
||||
case ExprNode::TYPE_STR_CASE:
|
||||
case ExprNode::TYPE_SUBSTRING_SIMILAR:
|
||||
case ExprNode::TYPE_TRIM:
|
||||
for (dsql_nod*** i = exprNode->dsqlChildNodes.begin();
|
||||
i != exprNode->dsqlChildNodes.end(); ++i)
|
||||
{
|
||||
@ -8659,9 +8650,6 @@ static void set_parameter_name( dsql_nod* par_node, const dsql_nod* fld_node, co
|
||||
return;
|
||||
|
||||
case nod_substr:
|
||||
case nod_trim:
|
||||
case nod_upcase:
|
||||
case nod_lowcase:
|
||||
case nod_extract:
|
||||
case nod_strlen:
|
||||
case nod_limit:
|
||||
@ -8993,9 +8981,6 @@ void DSQL_pretty(const dsql_nod* node, int column)
|
||||
case nod_substr:
|
||||
verb = "substr";
|
||||
break;
|
||||
case nod_trim:
|
||||
verb = "trim";
|
||||
break;
|
||||
case nod_update:
|
||||
verb = "update";
|
||||
break;
|
||||
@ -9005,12 +8990,6 @@ void DSQL_pretty(const dsql_nod* node, int column)
|
||||
case nod_unique:
|
||||
verb = "unique";
|
||||
break;
|
||||
case nod_upcase:
|
||||
verb = "upcase";
|
||||
break;
|
||||
case nod_lowcase:
|
||||
verb = "lowcase";
|
||||
break;
|
||||
case nod_via:
|
||||
verb = "via";
|
||||
break;
|
||||
|
@ -327,8 +327,6 @@ bool OPT_expression_equal2(thread_db* tdbb, CompilerScratch* csb,
|
||||
return true;
|
||||
|
||||
case nod_substr:
|
||||
case nod_trim:
|
||||
{
|
||||
if (node1->nod_count != node2->nod_count)
|
||||
return false;
|
||||
|
||||
@ -339,11 +337,6 @@ bool OPT_expression_equal2(thread_db* tdbb, CompilerScratch* csb,
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case nod_upcase:
|
||||
case nod_lowcase:
|
||||
return OPT_expression_equal2(tdbb, csb, node1->nod_arg[0], node2->nod_arg[0], stream);
|
||||
|
||||
case nod_cast:
|
||||
{
|
||||
|
@ -571,19 +571,6 @@ void CMP_get_desc(thread_db* tdbb, CompilerScratch* csb, jrd_nod* node, DSC* des
|
||||
return;
|
||||
}
|
||||
|
||||
case nod_upcase:
|
||||
case nod_lowcase:
|
||||
CMP_get_desc(tdbb, csb, node->nod_arg[0], desc);
|
||||
if (desc->dsc_dtype > dtype_varying && desc->dsc_dtype != dtype_blob)
|
||||
{
|
||||
desc->dsc_length = DSC_convert_to_text_length(desc->dsc_dtype);
|
||||
desc->dsc_dtype = dtype_text;
|
||||
desc->dsc_ttype() = ttype_ascii;
|
||||
desc->dsc_scale = 0;
|
||||
desc->dsc_flags = 0;
|
||||
}
|
||||
return;
|
||||
|
||||
case nod_dbkey:
|
||||
desc->dsc_dtype = dtype_dbkey;
|
||||
desc->dsc_length = type_lengths[dtype_dbkey];
|
||||
@ -741,32 +728,6 @@ void CMP_get_desc(thread_db* tdbb, CompilerScratch* csb, jrd_nod* node, DSC* des
|
||||
return;
|
||||
}
|
||||
|
||||
case nod_trim:
|
||||
CMP_get_desc(tdbb, csb, node->nod_arg[e_trim_value], desc);
|
||||
|
||||
if (node->nod_arg[e_trim_characters])
|
||||
{
|
||||
DSC desc1;
|
||||
CMP_get_desc(tdbb, csb, node->nod_arg[e_trim_characters], &desc1);
|
||||
desc->dsc_flags |= desc1.dsc_flags & DSC_null;
|
||||
}
|
||||
|
||||
if (desc->dsc_dtype != dtype_blob)
|
||||
{
|
||||
USHORT length = DSC_string_length(desc);
|
||||
|
||||
if (!DTYPE_IS_TEXT(desc->dsc_dtype))
|
||||
{
|
||||
desc->dsc_ttype() = ttype_ascii;
|
||||
desc->dsc_scale = 0;
|
||||
}
|
||||
|
||||
desc->dsc_dtype = dtype_varying;
|
||||
desc->dsc_length = length + sizeof(USHORT);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
case nod_variable:
|
||||
{
|
||||
const jrd_nod* value = node->nod_arg[e_var_variable];
|
||||
@ -1357,15 +1318,6 @@ jrd_nod* NodeCopier::copy(thread_db* tdbb, jrd_nod* input)
|
||||
node->nod_arg[e_strlen_type] = input->nod_arg[e_strlen_type];
|
||||
return (node);
|
||||
|
||||
case nod_trim:
|
||||
node = PAR_make_node(tdbb, e_trim_length);
|
||||
node->nod_count = input->nod_count;
|
||||
node->nod_type = input->nod_type;
|
||||
node->nod_arg[e_trim_characters] = copy(tdbb, input->nod_arg[e_trim_characters]);
|
||||
node->nod_arg[e_trim_value] = copy(tdbb, input->nod_arg[e_trim_value]);
|
||||
node->nod_arg[e_trim_specification] = input->nod_arg[e_trim_specification];
|
||||
return (node);
|
||||
|
||||
case nod_count:
|
||||
case nod_max:
|
||||
case nod_min:
|
||||
@ -3209,10 +3161,7 @@ jrd_nod* CMP_pass2(thread_db* tdbb, CompilerScratch* csb, jrd_nod* const node, j
|
||||
case nod_dbkey:
|
||||
case nod_rec_version:
|
||||
case nod_substr:
|
||||
case nod_trim:
|
||||
case nod_null:
|
||||
case nod_upcase:
|
||||
case nod_lowcase:
|
||||
case nod_prot_mask:
|
||||
case nod_lock_state:
|
||||
case nod_scalar:
|
||||
|
228
src/jrd/evl.cpp
228
src/jrd/evl.cpp
@ -124,13 +124,10 @@ static dsc* eval_statistical(thread_db*, const jrd_nod*, impure_value*);
|
||||
static dsc* extract(thread_db*, const jrd_nod*, impure_value*);
|
||||
static dsc* get_mask(thread_db*, const jrd_nod*, impure_value*);
|
||||
static dsc* lock_state(thread_db*, const jrd_nod*, impure_value*);
|
||||
static dsc* low_up_case(thread_db*, const dsc*, impure_value*,
|
||||
ULONG (TextType::*tt_str_to_case)(ULONG, const UCHAR*, ULONG, UCHAR*));
|
||||
static dsc* record_version(thread_db*, const jrd_nod*, impure_value*);
|
||||
static dsc* scalar(thread_db*, const jrd_nod*, impure_value*);
|
||||
static dsc* string_length(thread_db*, const jrd_nod*, impure_value*);
|
||||
static dsc* substring(thread_db*, impure_value*, dsc*, const dsc*, const dsc*);
|
||||
static dsc* trim(thread_db*, const jrd_nod*, impure_value*);
|
||||
|
||||
|
||||
dsc* EVL_assign_to(thread_db* tdbb, const jrd_nod* node)
|
||||
@ -536,9 +533,6 @@ dsc* EVL_expr(thread_db* tdbb, const jrd_nod* node)
|
||||
}
|
||||
return request->req_domain_validation;
|
||||
|
||||
case nod_trim:
|
||||
return trim(tdbb, node, impure);
|
||||
|
||||
default: // Shut up some compiler warnings
|
||||
break;
|
||||
|
||||
@ -576,12 +570,6 @@ dsc* EVL_expr(thread_db* tdbb, const jrd_nod* node)
|
||||
case nod_substr:
|
||||
return substring(tdbb, impure, values[0], values[1], values[2]);
|
||||
|
||||
case nod_upcase:
|
||||
return low_up_case(tdbb, values[0], impure, &TextType::str_to_upper);
|
||||
|
||||
case nod_lowcase:
|
||||
return low_up_case(tdbb, values[0], impure, &TextType::str_to_lower);
|
||||
|
||||
case nod_cast:
|
||||
return cast(tdbb, values[0], node, impure);
|
||||
|
||||
@ -1541,83 +1529,6 @@ static dsc* lock_state(thread_db* tdbb, const jrd_nod* node, impure_value* impur
|
||||
}
|
||||
|
||||
|
||||
static dsc* low_up_case(thread_db* tdbb, const dsc* value, impure_value* impure,
|
||||
ULONG (TextType::*tt_str_to_case)(ULONG, const UCHAR*, ULONG, UCHAR*))
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
* l o w _ u p _ c a s e
|
||||
*
|
||||
**************************************
|
||||
*
|
||||
* Functional description
|
||||
* Low/up case a string.
|
||||
*
|
||||
**************************************/
|
||||
SET_TDBB(tdbb);
|
||||
|
||||
TextType* textType = INTL_texttype_lookup(tdbb, value->getTextType());
|
||||
|
||||
if (value->isBlob())
|
||||
{
|
||||
EVL_make_value(tdbb, value, impure);
|
||||
|
||||
if (value->dsc_sub_type != isc_blob_text)
|
||||
return &impure->vlu_desc;
|
||||
|
||||
CharSet* charSet = textType->getCharSet();
|
||||
|
||||
blb* blob = BLB_open(tdbb, tdbb->getRequest()->req_transaction,
|
||||
reinterpret_cast<bid*>(value->dsc_address));
|
||||
|
||||
Firebird::HalfStaticArray<UCHAR, BUFFER_SMALL> buffer;
|
||||
|
||||
if (charSet->isMultiByte())
|
||||
buffer.getBuffer(blob->blb_length); // alloc space to put entire blob in memory
|
||||
|
||||
blb* newBlob = BLB_create(tdbb, tdbb->getRequest()->req_transaction,
|
||||
&impure->vlu_misc.vlu_bid);
|
||||
|
||||
while (!(blob->blb_flags & BLB_eof))
|
||||
{
|
||||
SLONG len = BLB_get_data(tdbb, blob, buffer.begin(), buffer.getCapacity(), false);
|
||||
|
||||
if (len)
|
||||
{
|
||||
len = (textType->*tt_str_to_case)(len, buffer.begin(), len, buffer.begin());
|
||||
BLB_put_data(tdbb, newBlob, buffer.begin(), len);
|
||||
}
|
||||
}
|
||||
|
||||
BLB_close(tdbb, newBlob);
|
||||
BLB_close(tdbb, blob);
|
||||
}
|
||||
else
|
||||
{
|
||||
UCHAR* ptr;
|
||||
VaryStr<32> temp;
|
||||
USHORT ttype;
|
||||
|
||||
dsc desc;
|
||||
desc.dsc_length = MOV_get_string_ptr(value, &ttype, &ptr, &temp, sizeof(temp));
|
||||
desc.dsc_dtype = dtype_text;
|
||||
desc.dsc_address = NULL;
|
||||
desc.setTextType(ttype);
|
||||
EVL_make_value(tdbb, &desc, impure);
|
||||
|
||||
ULONG len = (textType->*tt_str_to_case)(desc.dsc_length,
|
||||
ptr, desc.dsc_length, impure->vlu_desc.dsc_address);
|
||||
|
||||
if (len == INTL_BAD_STR_LENGTH)
|
||||
status_exception::raise(Arg::Gds(isc_arith_except));
|
||||
|
||||
impure->vlu_desc.dsc_length = (USHORT) len;
|
||||
}
|
||||
|
||||
return &impure->vlu_desc;
|
||||
}
|
||||
|
||||
|
||||
static dsc* record_version(thread_db* tdbb, const jrd_nod* node, impure_value* impure)
|
||||
{
|
||||
/**************************************
|
||||
@ -1869,142 +1780,3 @@ static dsc* substring(thread_db* tdbb, impure_value* impure,
|
||||
**************************************/
|
||||
return SysFunction::substring(tdbb, impure, value, offset_value, length_value);
|
||||
}
|
||||
|
||||
|
||||
static dsc* trim(thread_db* tdbb, const jrd_nod* node, impure_value* impure)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
* t r i m
|
||||
*
|
||||
**************************************
|
||||
*
|
||||
* Functional description
|
||||
* Perform trim function = TRIM([where what FROM] string)
|
||||
*
|
||||
**************************************/
|
||||
SET_TDBB(tdbb);
|
||||
|
||||
jrd_req* request = tdbb->getRequest();
|
||||
|
||||
const ULONG specification = (IPTR) node->nod_arg[e_trim_specification];
|
||||
|
||||
request->req_flags &= ~req_null;
|
||||
dsc* characters = (node->nod_arg[e_trim_characters] ? EVL_expr(tdbb, node->nod_arg[e_trim_characters]) : NULL);
|
||||
if (request->req_flags & req_null)
|
||||
return characters;
|
||||
|
||||
request->req_flags &= ~req_null;
|
||||
dsc* value = EVL_expr(tdbb, node->nod_arg[e_trim_value]);
|
||||
if (request->req_flags & req_null)
|
||||
return value;
|
||||
|
||||
USHORT ttype = INTL_TEXT_TYPE(*value);
|
||||
TextType* tt = INTL_texttype_lookup(tdbb, ttype);
|
||||
CharSet* cs = tt->getCharSet();
|
||||
|
||||
const UCHAR* charactersAddress;
|
||||
MoveBuffer charactersBuffer;
|
||||
USHORT charactersLength;
|
||||
|
||||
if (characters)
|
||||
{
|
||||
UCHAR* tempAddress = 0;
|
||||
charactersLength = MOV_make_string2(tdbb, characters, ttype, &tempAddress, charactersBuffer);
|
||||
charactersAddress = tempAddress;
|
||||
}
|
||||
else
|
||||
{
|
||||
charactersLength = tt->getCharSet()->getSpaceLength();
|
||||
charactersAddress = tt->getCharSet()->getSpace();
|
||||
}
|
||||
|
||||
Firebird::HalfStaticArray<UCHAR, BUFFER_SMALL> charactersCanonical;
|
||||
charactersCanonical.getBuffer(charactersLength / tt->getCharSet()->minBytesPerChar() * tt->getCanonicalWidth());
|
||||
const SLONG charactersCanonicalLen = tt->canonical(charactersLength, charactersAddress,
|
||||
charactersCanonical.getCount(), charactersCanonical.begin()) * tt->getCanonicalWidth();
|
||||
|
||||
Firebird::HalfStaticArray<UCHAR, BUFFER_SMALL> blobBuffer;
|
||||
MoveBuffer valueBuffer;
|
||||
UCHAR* valueAddress;
|
||||
ULONG valueLength;
|
||||
|
||||
if (value->isBlob())
|
||||
{
|
||||
// Source string is a blob, things get interesting.
|
||||
blb* blob = BLB_open(tdbb, tdbb->getRequest()->req_transaction,
|
||||
reinterpret_cast<bid*>(value->dsc_address));
|
||||
|
||||
// It's very difficult (and probably not very efficient) to trim a blob in chunks.
|
||||
// So go simple way and always read entire blob in memory.
|
||||
valueAddress = blobBuffer.getBuffer(blob->blb_length);
|
||||
valueLength = BLB_get_data(tdbb, blob, valueAddress, blob->blb_length, true);
|
||||
}
|
||||
else
|
||||
valueLength = MOV_make_string2(tdbb, value, ttype, &valueAddress, valueBuffer);
|
||||
|
||||
Firebird::HalfStaticArray<UCHAR, BUFFER_SMALL> valueCanonical;
|
||||
valueCanonical.getBuffer(valueLength / cs->minBytesPerChar() * tt->getCanonicalWidth());
|
||||
const SLONG valueCanonicalLen = tt->canonical(valueLength, valueAddress,
|
||||
valueCanonical.getCount(), valueCanonical.begin()) * tt->getCanonicalWidth();
|
||||
|
||||
SLONG offsetLead = 0;
|
||||
SLONG offsetTrail = valueCanonicalLen;
|
||||
|
||||
// CVC: Avoid endless loop with zero length trim chars.
|
||||
if (charactersCanonicalLen)
|
||||
{
|
||||
if (specification == blr_trim_both || specification == blr_trim_leading)
|
||||
{
|
||||
// CVC: Prevent surprises with offsetLead < valueCanonicalLen; it may fail.
|
||||
for (; offsetLead + charactersCanonicalLen <= valueCanonicalLen; offsetLead += charactersCanonicalLen)
|
||||
{
|
||||
if (memcmp(charactersCanonical.begin(), &valueCanonical[offsetLead], charactersCanonicalLen) != 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (specification == blr_trim_both || specification == blr_trim_trailing)
|
||||
{
|
||||
for (; offsetTrail - charactersCanonicalLen >= offsetLead; offsetTrail -= charactersCanonicalLen)
|
||||
{
|
||||
if (memcmp(charactersCanonical.begin(), &valueCanonical[offsetTrail - charactersCanonicalLen],
|
||||
charactersCanonicalLen) != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (value->isBlob())
|
||||
{
|
||||
// We have valueCanonical already allocated.
|
||||
// Use it to get the substring that will be written to the new blob.
|
||||
const ULONG len = cs->substring(valueLength, valueAddress,
|
||||
valueCanonical.getCapacity(), valueCanonical.begin(),
|
||||
offsetLead / tt->getCanonicalWidth(),
|
||||
(offsetTrail - offsetLead) / tt->getCanonicalWidth());
|
||||
|
||||
EVL_make_value(tdbb, value, impure);
|
||||
|
||||
blb* newBlob = BLB_create(tdbb, tdbb->getRequest()->req_transaction, &impure->vlu_misc.vlu_bid);
|
||||
|
||||
BLB_put_data(tdbb, newBlob, valueCanonical.begin(), len);
|
||||
|
||||
BLB_close(tdbb, newBlob);
|
||||
}
|
||||
else
|
||||
{
|
||||
dsc desc;
|
||||
desc.makeText(valueLength, ttype);
|
||||
EVL_make_value(tdbb, &desc, impure);
|
||||
|
||||
impure->vlu_desc.dsc_length = cs->substring(valueLength, valueAddress,
|
||||
impure->vlu_desc.dsc_length, impure->vlu_desc.dsc_address,
|
||||
offsetLead / tt->getCanonicalWidth(),
|
||||
(offsetTrail - offsetLead) / tt->getCanonicalWidth());
|
||||
}
|
||||
|
||||
return &impure->vlu_desc;
|
||||
}
|
||||
|
@ -326,12 +326,6 @@ const int e_strlen_type = 1;
|
||||
const int e_strlen_count = 1;
|
||||
const int e_strlen_length = 2;
|
||||
|
||||
const int e_trim_value = 0;
|
||||
const int e_trim_characters = 1;
|
||||
const int e_trim_specification = 2;
|
||||
const int e_trim_count = 2;
|
||||
const int e_trim_length = 3;
|
||||
|
||||
// nod_src_info
|
||||
const int e_src_info_line = 0;
|
||||
const int e_src_info_col = 1;
|
||||
|
@ -64,15 +64,12 @@ NODE(nod_from, from, "")
|
||||
NODE(nod_literal, literal, "")
|
||||
NODE(nod_scalar, scalar, "")
|
||||
NODE(nod_prot_mask, prot_mask, "")
|
||||
NODE(nod_upcase, upcase, "UPPER")
|
||||
NODE(nod_lock_state, lock_state, "")
|
||||
NODE(nod_null, null, "NULL")
|
||||
NODE(nod_substr, substr, "")
|
||||
NODE(nod_trim, trim, "")
|
||||
NODE(nod_cast, cast, "CAST")
|
||||
NODE(nod_rec_version, record_version, "RECORD VERSION")
|
||||
NODE(nod_extract, extract, "EXTRACT")
|
||||
NODE(nod_lowcase, lowcase, "LOWER")
|
||||
NODE(nod_strlen, strlen, "STRLEN")
|
||||
NODE(nod_domain_validation, domain_validation, "")
|
||||
NODE(nod_derived_expr, derived_expr, "derived_expr")
|
||||
|
@ -135,10 +135,7 @@ bool JrdNodeVisitor::visitChildren(const JrdNode& node)
|
||||
ret |= visit(jrdNode->nod_arg[e_strlen_value]);
|
||||
break;
|
||||
|
||||
case nod_upcase:
|
||||
case nod_lowcase:
|
||||
case nod_substr:
|
||||
case nod_trim:
|
||||
case nod_derived_expr:
|
||||
{
|
||||
jrd_nod* /*const*/* ptr = jrdNode->nod_arg;
|
||||
|
@ -2284,25 +2284,6 @@ jrd_nod* PAR_parse_node(thread_db* tdbb, CompilerScratch* csb, USHORT expected)
|
||||
*arg++ = PAR_parse_node(tdbb, csb, sub_type);
|
||||
break;
|
||||
|
||||
case blr_trim:
|
||||
{
|
||||
node->nod_count = e_trim_count;
|
||||
node->nod_arg[e_trim_specification] = (jrd_nod*)(U_IPTR) csb->csb_blr_reader.getByte();
|
||||
|
||||
BYTE trimWhat = csb->csb_blr_reader.getByte();
|
||||
|
||||
if (trimWhat == blr_trim_characters)
|
||||
node->nod_arg[e_trim_characters] = PAR_parse_node(tdbb, csb, sub_type);
|
||||
else
|
||||
{
|
||||
node->nod_arg[e_trim_characters] = NULL;
|
||||
--node->nod_count;
|
||||
}
|
||||
|
||||
node->nod_arg[e_trim_value] = PAR_parse_node(tdbb, csb, sub_type);
|
||||
break;
|
||||
}
|
||||
|
||||
case blr_prot_mask:
|
||||
case blr_assignment:
|
||||
*arg++ = PAR_parse_node(tdbb, csb, sub_type);
|
||||
@ -2311,8 +2292,6 @@ jrd_nod* PAR_parse_node(thread_db* tdbb, CompilerScratch* csb, USHORT expected)
|
||||
case blr_handler:
|
||||
case blr_loop:
|
||||
case blr_lock_state:
|
||||
case blr_upcase:
|
||||
case blr_lowcase:
|
||||
*arg++ = PAR_parse_node(tdbb, csb, sub_type);
|
||||
break;
|
||||
|
||||
|
@ -137,7 +137,7 @@ static const VERB verbs[] =
|
||||
PAIR(nod_class_exprnode_jrd, blr_negate, 1, 0, VALUE, VALUE),
|
||||
PAIR(nod_class_exprnode_jrd, blr_gen_id, 1, 0, VALUE, VALUE),
|
||||
PAIR(nod_prot_mask, blr_prot_mask, e_pro_length, 2, VALUE, VALUE),
|
||||
PAIR(nod_upcase, blr_upcase, 1, 1, VALUE, VALUE),
|
||||
PAIR(nod_class_exprnode_jrd, blr_upcase, 1, 0, VALUE, VALUE),
|
||||
PAIR(nod_lock_state, blr_lock_state, 1, 1, VALUE, VALUE),
|
||||
PAIR(nod_substr, blr_substring, 3, 3, VALUE, VALUE),
|
||||
PAIR(nod_class_exprnode_jrd, blr_subtract, 1, 0, VALUE, VALUE),
|
||||
@ -208,9 +208,9 @@ static const VERB verbs[] =
|
||||
PAIR(nod_class_exprnode_jrd, blr_current_role, 1, 0, VALUE, VALUE),
|
||||
PAIR(nod_dcl_cursor, blr_dcl_cursor, e_dcl_cur_length, 2, STATEMENT, OTHER),
|
||||
PAIR(nod_cursor_stmt, blr_cursor_stmt, e_cursor_stmt_length, 0, STATEMENT, OTHER),
|
||||
PAIR(nod_lowcase, blr_lowcase, 1, 1, VALUE, VALUE),
|
||||
PAIR(nod_class_exprnode_jrd, blr_lowcase, 1, 0, VALUE, VALUE),
|
||||
PAIR(nod_strlen, blr_strlen, e_strlen_length, e_strlen_count, VALUE, VALUE),
|
||||
PAIR(nod_trim, blr_trim, e_trim_length, e_trim_count, VALUE, VALUE),
|
||||
PAIR(nod_class_exprnode_jrd, blr_trim, 1, 0, VALUE, VALUE),
|
||||
PAIR(nod_init_variable, blr_init_variable, e_init_var_length, 0, STATEMENT, OTHER),
|
||||
PAIR(nod_class_exprnode_jrd, blr_sys_function, 1, 0, VALUE, VALUE),
|
||||
PAIR(nod_class_stmtnode_jrd, blr_auto_trans, 1, 0, STATEMENT, STATEMENT),
|
||||
|
Loading…
Reference in New Issue
Block a user