From ac3c00d503487c8b3ff0163e11a5e942a20e4900 Mon Sep 17 00:00:00 2001 From: asfernandes Date: Sat, 9 Oct 2010 18:39:45 +0000 Subject: [PATCH] 1) Refactored LOWER, UPPER and TRIM. 2) Fixed CORE-3174 - Expression index with TRIM may lead to incorrect indexed lookup --- src/dsql/ExprNodes.cpp | 509 +++++++++++++++++++++++++++++++++++++++++ src/dsql/ExprNodes.h | 65 ++++++ src/dsql/Nodes.h | 2 + src/dsql/Visitors.h | 3 - src/dsql/gen.cpp | 21 -- src/dsql/make.cpp | 55 ----- src/dsql/node.h | 3 - src/dsql/parse.y | 60 +++-- src/dsql/pass1.cpp | 25 +- src/jrd/Optimizer.cpp | 7 - src/jrd/cmp.cpp | 51 ----- src/jrd/evl.cpp | 228 ------------------ src/jrd/exe.h | 6 - src/jrd/nod.h | 3 - src/jrd/opt.cpp | 3 - src/jrd/par.cpp | 21 -- src/misc/blrtable.cpp | 6 +- 17 files changed, 610 insertions(+), 458 deletions(-) diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index 8d37209b9d..be787742f8 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -22,6 +22,7 @@ #include #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 regStrCaseNodeLower(blr_lowcase); +static RegisterNode regStrCaseNodeUpper(blr_upcase); + +StrCaseNode::StrCaseNode(MemoryPool& pool, UCHAR aBlrOp, dsql_nod* aArg) + : TypedNode(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& 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(); + 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(); + 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(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(value->dsc_address)); + + HalfStaticArray 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 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 regTrimNode(blr_trim); + +TrimNode::TrimNode(MemoryPool& pool, UCHAR aWhere, dsql_nod* aValue, dsql_nod* aTrimChars) + : TypedNode(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& 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(); + 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(); + 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(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 charactersCanonical; + charactersCanonical.getBuffer(charactersLength / tt->getCharSet()->minBytesPerChar() * + tt->getCanonicalWidth()); + const SLONG charactersCanonicalLen = tt->canonical(charactersLength, charactersAddress, + charactersCanonical.getCount(), charactersCanonical.begin()) * tt->getCanonicalWidth(); + + HalfStaticArray 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(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 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 regUdfCallNode1(blr_function); static RegisterNode regUdfCallNode2(blr_function2); diff --git a/src/dsql/ExprNodes.h b/src/dsql/ExprNodes.h index 77828724dc..12cd2d8e22 100644 --- a/src/dsql/ExprNodes.h +++ b/src/dsql/ExprNodes.h @@ -449,6 +449,37 @@ public: }; +class StrCaseNode : public TypedNode +{ +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& 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 arg; +}; + + class SubstringSimilarNode : public TypedNode { public: @@ -514,6 +545,40 @@ public: }; +class TrimNode : public TypedNode +{ +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& 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 value; + NestConst trimChars; // may be NULL +}; + + class UdfCallNode : public TypedNode { public: diff --git a/src/dsql/Nodes.h b/src/dsql/Nodes.h index e9df209725..7c0159c147 100644 --- a/src/dsql/Nodes.h +++ b/src/dsql/Nodes.h @@ -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 }; diff --git a/src/dsql/Visitors.h b/src/dsql/Visitors.h index c936ee3444..01e80a0233 100644 --- a/src/dsql/Visitors.h +++ b/src/dsql/Visitors.h @@ -520,9 +520,6 @@ inline bool DsqlNodeVisitor::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: diff --git a/src/dsql/gen.cpp b/src/dsql/gen.cpp index fa49846aad..7234728202 100644 --- a/src/dsql/gen.cpp +++ b/src/dsql/gen.cpp @@ -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]); diff --git a/src/dsql/make.cpp b/src/dsql/make.cpp index 26fe8f5f11..8d2858676f 100644 --- a/src/dsql/make.cpp +++ b/src/dsql/make.cpp @@ -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; diff --git a/src/dsql/node.h b/src/dsql/node.h index 58a2b0b99b..3b573d66ca 100644 --- a/src/dsql/node.h +++ b/src/dsql/node.h @@ -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, diff --git a/src/dsql/parse.y b/src/dsql/parse.y index e7769293b3..18be2b1b0a 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -799,7 +799,6 @@ inline void check_copy_incr(char*& to, const char ch, const char* const string) %type table_proc table_proc_inputs table_reference table_subquery tbl_reserve_options %type timestamp_part top tra_misc_options tra_timeout tran_opt tran_opt_list tran_opt_list_m %type trim_function -%type trim_specification %type time_precision_opt timestamp_precision_opt %type 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 system_function_special_syntax +%type trim_specification + // Predicates %type between_predicate comparison_predicate distinct_predicate %type 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 ')' diff --git a/src/dsql/pass1.cpp b/src/dsql/pass1.cpp index 417624a89d..df9c6f6652 100644 --- a/src/dsql/pass1.cpp +++ b/src/dsql/pass1.cpp @@ -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; diff --git a/src/jrd/Optimizer.cpp b/src/jrd/Optimizer.cpp index fe3ea41cff..b5dd24f0a6 100644 --- a/src/jrd/Optimizer.cpp +++ b/src/jrd/Optimizer.cpp @@ -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: { diff --git a/src/jrd/cmp.cpp b/src/jrd/cmp.cpp index 826ba37bfc..acf4d6e0f7 100644 --- a/src/jrd/cmp.cpp +++ b/src/jrd/cmp.cpp @@ -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: diff --git a/src/jrd/evl.cpp b/src/jrd/evl.cpp index 032b0491ed..565f8f7b68 100644 --- a/src/jrd/evl.cpp +++ b/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(value->dsc_address)); - - Firebird::HalfStaticArray 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 charactersCanonical; - charactersCanonical.getBuffer(charactersLength / tt->getCharSet()->minBytesPerChar() * tt->getCanonicalWidth()); - const SLONG charactersCanonicalLen = tt->canonical(charactersLength, charactersAddress, - charactersCanonical.getCount(), charactersCanonical.begin()) * tt->getCanonicalWidth(); - - Firebird::HalfStaticArray 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(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 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; -} diff --git a/src/jrd/exe.h b/src/jrd/exe.h index 3fbf6d017e..b7a78f6383 100644 --- a/src/jrd/exe.h +++ b/src/jrd/exe.h @@ -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; diff --git a/src/jrd/nod.h b/src/jrd/nod.h index aac46acc55..da54861faf 100644 --- a/src/jrd/nod.h +++ b/src/jrd/nod.h @@ -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") diff --git a/src/jrd/opt.cpp b/src/jrd/opt.cpp index 6ef2ff635f..5960d2fdd5 100644 --- a/src/jrd/opt.cpp +++ b/src/jrd/opt.cpp @@ -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; diff --git a/src/jrd/par.cpp b/src/jrd/par.cpp index 62b1afa718..11eddac0cb 100644 --- a/src/jrd/par.cpp +++ b/src/jrd/par.cpp @@ -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; diff --git a/src/misc/blrtable.cpp b/src/misc/blrtable.cpp index d2f1c197bd..c98916288d 100644 --- a/src/misc/blrtable.cpp +++ b/src/misc/blrtable.cpp @@ -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),