mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-22 17:23: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 <math.h>
|
||||||
#include "../jrd/common.h"
|
#include "../jrd/common.h"
|
||||||
#include "../common/classes/FpeControl.h"
|
#include "../common/classes/FpeControl.h"
|
||||||
|
#include "../common/classes/VaryStr.h"
|
||||||
#include "../dsql/ExprNodes.h"
|
#include "../dsql/ExprNodes.h"
|
||||||
#include "../dsql/node.h"
|
#include "../dsql/node.h"
|
||||||
#include "../jrd/align.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);
|
static RegisterNode<SubstringSimilarNode> regSubstringSimilarNode(blr_substring_similar);
|
||||||
|
|
||||||
SubstringSimilarNode::SubstringSimilarNode(MemoryPool& pool, dsql_nod* aExpr, dsql_nod* aPattern,
|
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> regUdfCallNode1(blr_function);
|
||||||
static RegisterNode<UdfCallNode> regUdfCallNode2(blr_function2);
|
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>
|
class SubstringSimilarNode : public TypedNode<ValueExprNode, ExprNode::TYPE_SUBSTRING_SIMILAR>
|
||||||
{
|
{
|
||||||
public:
|
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>
|
class UdfCallNode : public TypedNode<ValueExprNode, ExprNode::TYPE_UDF_CALL>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -272,8 +272,10 @@ public:
|
|||||||
TYPE_OVER,
|
TYPE_OVER,
|
||||||
TYPE_PARAMETER,
|
TYPE_PARAMETER,
|
||||||
TYPE_RSE_BOOL,
|
TYPE_RSE_BOOL,
|
||||||
|
TYPE_STR_CASE,
|
||||||
TYPE_SUBSTRING_SIMILAR,
|
TYPE_SUBSTRING_SIMILAR,
|
||||||
TYPE_SYSFUNC_CALL,
|
TYPE_SYSFUNC_CALL,
|
||||||
|
TYPE_TRIM,
|
||||||
TYPE_UDF_CALL,
|
TYPE_UDF_CALL,
|
||||||
TYPE_VALUE_IF
|
TYPE_VALUE_IF
|
||||||
};
|
};
|
||||||
|
@ -520,9 +520,6 @@ inline bool DsqlNodeVisitor<T, T2>::visitChildren(T node)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case nod_substr:
|
case nod_substr:
|
||||||
case nod_trim:
|
|
||||||
case nod_upcase:
|
|
||||||
case nod_lowcase:
|
|
||||||
case nod_extract:
|
case nod_extract:
|
||||||
case nod_strlen:
|
case nod_strlen:
|
||||||
case nod_simple_case:
|
case nod_simple_case:
|
||||||
|
@ -311,12 +311,6 @@ void GEN_expr(DsqlCompilerScratch* dsqlScratch, dsql_nod* node)
|
|||||||
blr_operator = blr_via;
|
blr_operator = blr_via;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case nod_upcase:
|
|
||||||
blr_operator = blr_upcase;
|
|
||||||
break;
|
|
||||||
case nod_lowcase:
|
|
||||||
blr_operator = blr_lowcase;
|
|
||||||
break;
|
|
||||||
case nod_substr:
|
case nod_substr:
|
||||||
blr_operator = blr_substring;
|
blr_operator = blr_substring;
|
||||||
break;
|
break;
|
||||||
@ -333,21 +327,6 @@ void GEN_expr(DsqlCompilerScratch* dsqlScratch, dsql_nod* node)
|
|||||||
gen_searched_case(dsqlScratch, node);
|
gen_searched_case(dsqlScratch, node);
|
||||||
return;
|
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:
|
case nod_assign:
|
||||||
dsqlScratch->appendUChar(blr_assignment);
|
dsqlScratch->appendUChar(blr_assignment);
|
||||||
GEN_expr(dsqlScratch, node->nod_arg[0]);
|
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);
|
MAKE_desc(dsqlScratch, desc, node->nod_arg[e_derived_field_value], null_replacement);
|
||||||
return;
|
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:
|
case nod_substr:
|
||||||
MAKE_desc(dsqlScratch, &desc1, node->nod_arg[0], null_replacement);
|
MAKE_desc(dsqlScratch, &desc1, node->nod_arg[0], null_replacement);
|
||||||
MAKE_desc(dsqlScratch, &desc2, node->nod_arg[1], 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);
|
DSqlDataTypeUtil(dsqlScratch).makeSubstr(desc, &desc1, &desc2, &desc3);
|
||||||
return;
|
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:
|
case nod_cast:
|
||||||
field = (dsql_fld*) node->nod_arg[e_cast_target];
|
field = (dsql_fld*) node->nod_arg[e_cast_target];
|
||||||
MAKE_desc_from_field(desc, field);
|
MAKE_desc_from_field(desc, field);
|
||||||
@ -1226,18 +1180,9 @@ void MAKE_parameter_names(dsql_par* parameter, const dsql_nod* item)
|
|||||||
case nod_substr:
|
case nod_substr:
|
||||||
name_alias = "SUBSTRING";
|
name_alias = "SUBSTRING";
|
||||||
break;
|
break;
|
||||||
case nod_trim:
|
|
||||||
name_alias = "TRIM";
|
|
||||||
break;
|
|
||||||
case nod_cast:
|
case nod_cast:
|
||||||
name_alias = "CAST";
|
name_alias = "CAST";
|
||||||
break;
|
break;
|
||||||
case nod_upcase:
|
|
||||||
name_alias = "UPPER";
|
|
||||||
break;
|
|
||||||
case nod_lowcase:
|
|
||||||
name_alias = "LOWER";
|
|
||||||
break;
|
|
||||||
case nod_extract:
|
case nod_extract:
|
||||||
name_alias = "EXTRACT";
|
name_alias = "EXTRACT";
|
||||||
break;
|
break;
|
||||||
|
@ -138,8 +138,6 @@ enum nod_t
|
|||||||
nod_null,
|
nod_null,
|
||||||
nod_dbkey,
|
nod_dbkey,
|
||||||
nod_cast,
|
nod_cast,
|
||||||
nod_upcase,
|
|
||||||
nod_lowcase,
|
|
||||||
nod_collate,
|
nod_collate,
|
||||||
nod_get_segment, // blobs
|
nod_get_segment, // blobs
|
||||||
nod_put_segment,
|
nod_put_segment,
|
||||||
@ -212,7 +210,6 @@ enum nod_t
|
|||||||
nod_query_spec,
|
nod_query_spec,
|
||||||
nod_mod_udf,
|
nod_mod_udf,
|
||||||
nod_strlen,
|
nod_strlen,
|
||||||
nod_trim,
|
|
||||||
nod_returning,
|
nod_returning,
|
||||||
nod_tra_misc,
|
nod_tra_misc,
|
||||||
nod_lock_timeout,
|
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> 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> timestamp_part top tra_misc_options tra_timeout tran_opt tran_opt_list tran_opt_list_m
|
||||||
%type <legacyNode> trim_function
|
%type <legacyNode> trim_function
|
||||||
%type <legacyNode> trim_specification
|
|
||||||
%type <uintVal> time_precision_opt timestamp_precision_opt
|
%type <uintVal> time_precision_opt timestamp_precision_opt
|
||||||
|
|
||||||
%type <legacyNode> u_constant u_numeric_constant udf udf_data_type udf_decl_clause undo_savepoint
|
%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 <sysFuncCallNode> system_function_special_syntax
|
||||||
|
|
||||||
|
%type <blrOp> trim_specification
|
||||||
|
|
||||||
// Predicates
|
// Predicates
|
||||||
%type <boolExprNode> between_predicate comparison_predicate distinct_predicate
|
%type <boolExprNode> between_predicate comparison_predicate distinct_predicate
|
||||||
%type <boolExprNode> exists_predicate in_predicate binary_pattern_predicate ternary_pattern_predicate null_predicate 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); }
|
{ $$ = FB_NEW(getPool()) SysFuncCallNode(getPool(), toName($1), $3); }
|
||||||
;
|
;
|
||||||
|
|
||||||
string_value_function : substring_function
|
string_value_function
|
||||||
| trim_function
|
: substring_function
|
||||||
| KW_UPPER '(' value ')'
|
| trim_function
|
||||||
{ $$ = make_node (nod_upcase, 1, $3); }
|
| KW_UPPER '(' value ')'
|
||||||
| KW_LOWER '(' value ')'
|
{ $$ = makeClassNode(FB_NEW(getPool()) StrCaseNode(getPool(), blr_upcase, $3)); }
|
||||||
{ $$ = make_node (nod_lowcase, 1, $3); }
|
| KW_LOWER '(' value ')'
|
||||||
;
|
{ $$ = makeClassNode(FB_NEW(getPool()) StrCaseNode(getPool(), blr_lowcase, $3)); }
|
||||||
|
;
|
||||||
|
|
||||||
substring_function
|
substring_function
|
||||||
: SUBSTRING '(' value FROM value string_length_opt ')'
|
: SUBSTRING '(' value FROM value string_length_opt ')'
|
||||||
@ -5706,31 +5708,27 @@ substring_function
|
|||||||
{ $$ = makeClassNode(FB_NEW(getPool()) SubstringSimilarNode(getPool(), $3, $5, $7)); }
|
{ $$ = makeClassNode(FB_NEW(getPool()) SubstringSimilarNode(getPool(), $3, $5, $7)); }
|
||||||
;
|
;
|
||||||
|
|
||||||
string_length_opt : FOR value
|
string_length_opt
|
||||||
{ $$ = $2; }
|
: { $$ = MAKE_const_slong(SHRT_POS_MAX); }
|
||||||
|
|
| FOR value { $$ = $2; }
|
||||||
{ $$ = MAKE_const_slong (SHRT_POS_MAX); }
|
;
|
||||||
;
|
|
||||||
|
|
||||||
trim_function : TRIM '(' trim_specification value FROM value ')'
|
trim_function
|
||||||
{ $$ = make_node (nod_trim, (int) e_trim_count, $3, $4, $6); }
|
: TRIM '(' trim_specification value FROM value ')'
|
||||||
| TRIM '(' value FROM value ')'
|
{ $$ = makeClassNode(FB_NEW(getPool()) TrimNode(getPool(), $3, $6, $4)); }
|
||||||
{ $$ = make_node (nod_trim, (int) e_trim_count,
|
| TRIM '(' value FROM value ')'
|
||||||
MAKE_const_slong (blr_trim_both), $3, $5); }
|
{ $$ = makeClassNode(FB_NEW(getPool()) TrimNode(getPool(), blr_trim_both, $5, $3)); }
|
||||||
| TRIM '(' trim_specification FROM value ')'
|
| TRIM '(' trim_specification FROM value ')'
|
||||||
{ $$ = make_node (nod_trim, (int) e_trim_count, $3, NULL, $5); }
|
{ $$ = makeClassNode(FB_NEW(getPool()) TrimNode(getPool(), $3, $5, NULL)); }
|
||||||
| TRIM '(' value ')'
|
| TRIM '(' value ')'
|
||||||
{ $$ = make_node (nod_trim, (int) e_trim_count,
|
{ $$ = makeClassNode(FB_NEW(getPool()) TrimNode(getPool(), blr_trim_both, $3, NULL)); }
|
||||||
MAKE_const_slong (blr_trim_both), NULL, $3); }
|
;
|
||||||
;
|
|
||||||
|
|
||||||
trim_specification : BOTH
|
trim_specification
|
||||||
{ $$ = MAKE_const_slong (blr_trim_both); }
|
: BOTH { $$ = blr_trim_both; }
|
||||||
| TRAILING
|
| TRAILING { $$ = blr_trim_trailing; }
|
||||||
{ $$ = MAKE_const_slong (blr_trim_trailing); }
|
| LEADING { $$ = blr_trim_leading; }
|
||||||
| LEADING
|
;
|
||||||
{ $$ = MAKE_const_slong (blr_trim_leading); }
|
|
||||||
;
|
|
||||||
|
|
||||||
udf
|
udf
|
||||||
: symbol_UDF_call_name '(' value_list ')'
|
: symbol_UDF_call_name '(' value_list ')'
|
||||||
|
@ -1575,14 +1575,6 @@ dsql_nod* PASS1_node(DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
|
|||||||
|
|
||||||
break;
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -8532,9 +8524,6 @@ bool PASS1_set_parameter_type(DsqlCompilerScratch* dsqlScratch, dsql_nod* in_nod
|
|||||||
}
|
}
|
||||||
|
|
||||||
case nod_substr:
|
case nod_substr:
|
||||||
case nod_trim:
|
|
||||||
case nod_upcase:
|
|
||||||
case nod_lowcase:
|
|
||||||
case nod_extract:
|
case nod_extract:
|
||||||
case nod_limit:
|
case nod_limit:
|
||||||
case nod_rows:
|
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_ARITHMETIC:
|
||||||
case ExprNode::TYPE_CONCATENATE:
|
case ExprNode::TYPE_CONCATENATE:
|
||||||
case ExprNode::TYPE_NEGATE:
|
case ExprNode::TYPE_NEGATE:
|
||||||
|
case ExprNode::TYPE_STR_CASE:
|
||||||
case ExprNode::TYPE_SUBSTRING_SIMILAR:
|
case ExprNode::TYPE_SUBSTRING_SIMILAR:
|
||||||
|
case ExprNode::TYPE_TRIM:
|
||||||
for (dsql_nod*** i = exprNode->dsqlChildNodes.begin();
|
for (dsql_nod*** i = exprNode->dsqlChildNodes.begin();
|
||||||
i != exprNode->dsqlChildNodes.end(); ++i)
|
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;
|
return;
|
||||||
|
|
||||||
case nod_substr:
|
case nod_substr:
|
||||||
case nod_trim:
|
|
||||||
case nod_upcase:
|
|
||||||
case nod_lowcase:
|
|
||||||
case nod_extract:
|
case nod_extract:
|
||||||
case nod_strlen:
|
case nod_strlen:
|
||||||
case nod_limit:
|
case nod_limit:
|
||||||
@ -8993,9 +8981,6 @@ void DSQL_pretty(const dsql_nod* node, int column)
|
|||||||
case nod_substr:
|
case nod_substr:
|
||||||
verb = "substr";
|
verb = "substr";
|
||||||
break;
|
break;
|
||||||
case nod_trim:
|
|
||||||
verb = "trim";
|
|
||||||
break;
|
|
||||||
case nod_update:
|
case nod_update:
|
||||||
verb = "update";
|
verb = "update";
|
||||||
break;
|
break;
|
||||||
@ -9005,12 +8990,6 @@ void DSQL_pretty(const dsql_nod* node, int column)
|
|||||||
case nod_unique:
|
case nod_unique:
|
||||||
verb = "unique";
|
verb = "unique";
|
||||||
break;
|
break;
|
||||||
case nod_upcase:
|
|
||||||
verb = "upcase";
|
|
||||||
break;
|
|
||||||
case nod_lowcase:
|
|
||||||
verb = "lowcase";
|
|
||||||
break;
|
|
||||||
case nod_via:
|
case nod_via:
|
||||||
verb = "via";
|
verb = "via";
|
||||||
break;
|
break;
|
||||||
|
@ -327,8 +327,6 @@ bool OPT_expression_equal2(thread_db* tdbb, CompilerScratch* csb,
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
case nod_substr:
|
case nod_substr:
|
||||||
case nod_trim:
|
|
||||||
{
|
|
||||||
if (node1->nod_count != node2->nod_count)
|
if (node1->nod_count != node2->nod_count)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -339,11 +337,6 @@ bool OPT_expression_equal2(thread_db* tdbb, CompilerScratch* csb,
|
|||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
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:
|
case nod_cast:
|
||||||
{
|
{
|
||||||
|
@ -571,19 +571,6 @@ void CMP_get_desc(thread_db* tdbb, CompilerScratch* csb, jrd_nod* node, DSC* des
|
|||||||
return;
|
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:
|
case nod_dbkey:
|
||||||
desc->dsc_dtype = dtype_dbkey;
|
desc->dsc_dtype = dtype_dbkey;
|
||||||
desc->dsc_length = type_lengths[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;
|
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:
|
case nod_variable:
|
||||||
{
|
{
|
||||||
const jrd_nod* value = node->nod_arg[e_var_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];
|
node->nod_arg[e_strlen_type] = input->nod_arg[e_strlen_type];
|
||||||
return (node);
|
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_count:
|
||||||
case nod_max:
|
case nod_max:
|
||||||
case nod_min:
|
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_dbkey:
|
||||||
case nod_rec_version:
|
case nod_rec_version:
|
||||||
case nod_substr:
|
case nod_substr:
|
||||||
case nod_trim:
|
|
||||||
case nod_null:
|
case nod_null:
|
||||||
case nod_upcase:
|
|
||||||
case nod_lowcase:
|
|
||||||
case nod_prot_mask:
|
case nod_prot_mask:
|
||||||
case nod_lock_state:
|
case nod_lock_state:
|
||||||
case nod_scalar:
|
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* extract(thread_db*, const jrd_nod*, impure_value*);
|
||||||
static dsc* get_mask(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* 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* record_version(thread_db*, const jrd_nod*, impure_value*);
|
||||||
static dsc* scalar(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* string_length(thread_db*, const jrd_nod*, impure_value*);
|
||||||
static dsc* substring(thread_db*, impure_value*, dsc*, const dsc*, const dsc*);
|
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)
|
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;
|
return request->req_domain_validation;
|
||||||
|
|
||||||
case nod_trim:
|
|
||||||
return trim(tdbb, node, impure);
|
|
||||||
|
|
||||||
default: // Shut up some compiler warnings
|
default: // Shut up some compiler warnings
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -576,12 +570,6 @@ dsc* EVL_expr(thread_db* tdbb, const jrd_nod* node)
|
|||||||
case nod_substr:
|
case nod_substr:
|
||||||
return substring(tdbb, impure, values[0], values[1], values[2]);
|
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:
|
case nod_cast:
|
||||||
return cast(tdbb, values[0], node, impure);
|
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)
|
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);
|
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_count = 1;
|
||||||
const int e_strlen_length = 2;
|
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
|
// nod_src_info
|
||||||
const int e_src_info_line = 0;
|
const int e_src_info_line = 0;
|
||||||
const int e_src_info_col = 1;
|
const int e_src_info_col = 1;
|
||||||
|
@ -64,15 +64,12 @@ NODE(nod_from, from, "")
|
|||||||
NODE(nod_literal, literal, "")
|
NODE(nod_literal, literal, "")
|
||||||
NODE(nod_scalar, scalar, "")
|
NODE(nod_scalar, scalar, "")
|
||||||
NODE(nod_prot_mask, prot_mask, "")
|
NODE(nod_prot_mask, prot_mask, "")
|
||||||
NODE(nod_upcase, upcase, "UPPER")
|
|
||||||
NODE(nod_lock_state, lock_state, "")
|
NODE(nod_lock_state, lock_state, "")
|
||||||
NODE(nod_null, null, "NULL")
|
NODE(nod_null, null, "NULL")
|
||||||
NODE(nod_substr, substr, "")
|
NODE(nod_substr, substr, "")
|
||||||
NODE(nod_trim, trim, "")
|
|
||||||
NODE(nod_cast, cast, "CAST")
|
NODE(nod_cast, cast, "CAST")
|
||||||
NODE(nod_rec_version, record_version, "RECORD VERSION")
|
NODE(nod_rec_version, record_version, "RECORD VERSION")
|
||||||
NODE(nod_extract, extract, "EXTRACT")
|
NODE(nod_extract, extract, "EXTRACT")
|
||||||
NODE(nod_lowcase, lowcase, "LOWER")
|
|
||||||
NODE(nod_strlen, strlen, "STRLEN")
|
NODE(nod_strlen, strlen, "STRLEN")
|
||||||
NODE(nod_domain_validation, domain_validation, "")
|
NODE(nod_domain_validation, domain_validation, "")
|
||||||
NODE(nod_derived_expr, derived_expr, "derived_expr")
|
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]);
|
ret |= visit(jrdNode->nod_arg[e_strlen_value]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case nod_upcase:
|
|
||||||
case nod_lowcase:
|
|
||||||
case nod_substr:
|
case nod_substr:
|
||||||
case nod_trim:
|
|
||||||
case nod_derived_expr:
|
case nod_derived_expr:
|
||||||
{
|
{
|
||||||
jrd_nod* /*const*/* ptr = jrdNode->nod_arg;
|
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);
|
*arg++ = PAR_parse_node(tdbb, csb, sub_type);
|
||||||
break;
|
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_prot_mask:
|
||||||
case blr_assignment:
|
case blr_assignment:
|
||||||
*arg++ = PAR_parse_node(tdbb, csb, sub_type);
|
*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_handler:
|
||||||
case blr_loop:
|
case blr_loop:
|
||||||
case blr_lock_state:
|
case blr_lock_state:
|
||||||
case blr_upcase:
|
|
||||||
case blr_lowcase:
|
|
||||||
*arg++ = PAR_parse_node(tdbb, csb, sub_type);
|
*arg++ = PAR_parse_node(tdbb, csb, sub_type);
|
||||||
break;
|
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_negate, 1, 0, VALUE, VALUE),
|
||||||
PAIR(nod_class_exprnode_jrd, blr_gen_id, 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_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_lock_state, blr_lock_state, 1, 1, VALUE, VALUE),
|
||||||
PAIR(nod_substr, blr_substring, 3, 3, VALUE, VALUE),
|
PAIR(nod_substr, blr_substring, 3, 3, VALUE, VALUE),
|
||||||
PAIR(nod_class_exprnode_jrd, blr_subtract, 1, 0, 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_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_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_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_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_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_exprnode_jrd, blr_sys_function, 1, 0, VALUE, VALUE),
|
||||||
PAIR(nod_class_stmtnode_jrd, blr_auto_trans, 1, 0, STATEMENT, STATEMENT),
|
PAIR(nod_class_stmtnode_jrd, blr_auto_trans, 1, 0, STATEMENT, STATEMENT),
|
||||||
|
Loading…
Reference in New Issue
Block a user