From c90557be0a9ae50a484db4a86f58d542dd9a9a4d Mon Sep 17 00:00:00 2001 From: SohamBhattacharjee Date: Wed, 18 Sep 2024 14:10:17 +0530 Subject: [PATCH 01/43] commit for new added test --- .../NonPublished/ECDbIdSetVirtualTable.cpp | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp diff --git a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp new file mode 100644 index 00000000000..1e872f55aca --- /dev/null +++ b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp @@ -0,0 +1,175 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Bentley Systems, Incorporated. All rights reserved. +* See LICENSE.md in the repository root for full copyright notice. +*--------------------------------------------------------------------------------------------*/ +#include "ECDbPublishedTests.h" + +USING_NAMESPACE_BENTLEY_EC + +BEGIN_ECDBUNITTESTS_NAMESPACE + +//--------------------------------------------------------------------------------------- +// @bsiclass +//+---------------+---------------+---------------+---------------+---------------+------ +struct ECDbIdSetVirtualTableTests : ECDbTestFixture {}; +//======================================================================================= +//! Virtual Table to tokenize string +// @bsiclass +//======================================================================================= +struct IdSetModule : ECDbModule { + struct IdSetTable : ECDbVirtualTable { + struct IdSetCursor : ECDbCursor { + + private: + Utf8String m_text; + bset m_idSet; + bset::iterator m_index; + + public: + IdSetCursor(IdSetTable& vt): ECDbCursor(vt){} + bool Eof() final { return m_index == m_idSet.end() ; } + DbResult Next() final { + ++m_index; + return BE_SQLITE_OK; + } + DbResult GetColumn(int i, Context& ctx) final { + ctx.SetResultInt64(*m_index); + return BE_SQLITE_OK; + } + DbResult GetRowId(int64_t& rowId) final { + rowId = *m_index; + return BE_SQLITE_OK; + } + DbResult Filter(int idxNum, const char *idxStr, int argc, DbValue* argv) final { + int i = 0; + if( idxNum & 1 ){ + m_text = argv[i++].GetValueText(); + }else{ + m_text = ""; + } + // Parse String to Js Document and iterate through the array and insert int hex ids as int64 in uniqueIds set. + BeJsDocument doc; + doc.Parse(m_text.c_str()); + doc.ForEachArrayMember([&](BeJsValue::ArrayIndex, BeJsConst k1) + { + m_idSet.insert(k1.asInt64()); + return false; }); + + m_index = m_idSet.begin(); + return BE_SQLITE_OK; + } + }; + public: + IdSetTable(IdSetModule& module): ECDbVirtualTable(module) {} + DbResult Open(DbCursor*& cur) override { + cur = new IdSetCursor(*this); + return BE_SQLITE_OK; + } + DbResult BestIndex(IndexInfo& indexInfo) final { + int i, j; /* Loop over constraints */ + int idxNum = 0; /* The query plan bitmask */ + int unusableMask = 0; /* Mask of unusable constraints */ + int nArg = 0; /* Number of arguments that seriesFilter() expects */ + int aIdx[2]; /* Constraints on start, stop, and step */ + const int SQLITE_SERIES_CONSTRAINT_VERIFY = 0; + aIdx[0] = aIdx[1] = -1; + int nConstraint = indexInfo.GetConstraintCount(); + + for(i=0; iGetColumn()< 0) continue; + iCol = pConstraint->GetColumn(); + iMask = 1 << iCol; + if (!pConstraint->IsUsable()){ + unusableMask |= iMask; + continue; + } else if (pConstraint->GetOp() == IndexInfo::Operator::EQ ){ + idxNum |= iMask; + aIdx[iCol] = i; + } + } + for( i = 0; i < 2; i++) { + if( (j = aIdx[i]) >= 0 ) { + indexInfo.GetConstraintUsage(j)->SetArgvIndex(++nArg); + indexInfo.GetConstraintUsage(j)->SetOmit(!SQLITE_SERIES_CONSTRAINT_VERIFY); + } + } + + if ((unusableMask & ~idxNum)!=0 ){ + return BE_SQLITE_CONSTRAINT; + } + + indexInfo.SetEstimatedCost(2.0); + indexInfo.SetEstimatedRows(1000); + if( indexInfo.GetIndexOrderByCount() >= 1 && indexInfo.GetOrderBy(0)->GetColumn() == 0 ) { + if( indexInfo.GetOrderBy(0) ->GetDesc()){ + idxNum |= 8; + } else { + idxNum |= 16; + } + indexInfo.SetOrderByConsumed(true); + } + indexInfo.SetIdxNum(idxNum); + return BE_SQLITE_OK; + } + }; + public: + IdSetModule(ECDbR db): ECDbModule( + db, + "IdSet", + "CREATE TABLE x(id hidden)", + R"xml( + + + + + + + + + + + + )xml") {} + DbResult Connect(DbVirtualTable*& out, Config& conf, int argc, const char* const* argv) final { + out = new IdSetTable(*this); + conf.SetTag(Config::Tags::Innocuous); + return BE_SQLITE_OK; + } +}; + +//--------------------------------------------------------------------------------------- +// @bsimethod +//+---------------+---------------+---------------+---------------+---------------+------ +TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { + ASSERT_EQ(BE_SQLITE_OK, SetupECDb("vtab.ecdb")); + (new IdSetModule(m_ecdb))->Register(); + std::vector hexIds = std::vector{"0x1", "0x2", "0x3", "4", "5"}; + Utf8String jsonArrayString = "["; + int k = 0; + for (k; k < hexIds.size() - 1; k++) + { + jsonArrayString += ("\"" + hexIds[k] + "\", "); + } + jsonArrayString += ("\"" + hexIds[k] + "\"]"); + + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + stmt.BindText(1, jsonArrayString.c_str(), IECSqlBinder::MakeCopy::No); + + int i = 0; + while (stmt.Step() == BE_SQLITE_ROW) + { + ASSERT_EQ(BeStringUtilities::ParseHex(hexIds[i++].c_str()), stmt.GetValueInt64(0)); + } + ASSERT_EQ(i, hexIds.size()); +} + + +END_ECDBUNITTESTS_NAMESPACE From f919765c03f4f97bddffec70ee756620b8ce849d Mon Sep 17 00:00:00 2001 From: SohamBhattacharjee Date: Fri, 20 Sep 2024 12:19:23 +0530 Subject: [PATCH 02/43] commit 2 --- iModelCore/ECDb/ECDb/BuiltInVTabs.cpp | 171 ++++++- iModelCore/ECDb/ECDb/BuiltInVTabs.h | 55 +++ iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp | 7 + iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp | 15 + .../NonPublished/ECDbIdSetVirtualTable.cpp | 421 ++++++++++++------ 5 files changed, 527 insertions(+), 142 deletions(-) diff --git a/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp b/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp index 35df43c554f..8c42da9808a 100644 --- a/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp +++ b/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp @@ -151,8 +151,175 @@ DbResult ClassPropsModule::Connect(DbVirtualTable*& out, Config& conf, int argc, return BE_SQLITE_OK; } +/*IdSet Virtual Table*/ + +//--------------------------------------------------------------------------------------- +// @bsimethod +//--------------------------------------------------------------------------------------- +DbResult IdSetModule::IdSetTable::IdSetCursor::Next() { + ++m_index; + return BE_SQLITE_OK; +} + +//--------------------------------------------------------------------------------------- +// @bsimethod +//--------------------------------------------------------------------------------------- +DbResult IdSetModule::IdSetTable::IdSetCursor::GetRowId(int64_t& rowId) { + rowId = (*m_index).GetValue(); + return BE_SQLITE_OK; +} + +//--------------------------------------------------------------------------------------- +// @bsimethod +//--------------------------------------------------------------------------------------- +DbResult IdSetModule::IdSetTable::IdSetCursor::GetColumn(int i, Context& ctx) { + ctx.SetResultInt64((*m_index).GetValue()); + return BE_SQLITE_OK; +} + +//--------------------------------------------------------------------------------------- +// @bsimethod +//--------------------------------------------------------------------------------------- +DbResult IdSetModule::IdSetTable::IdSetCursor::Filter(int idxNum, const char *idxStr, int argc, DbValue* argv) { + int recompute = false; + if( idxNum & 1 ){ + m_ArgType = argv[0].GetValueType(); + if(m_ArgType == DbValueType::TextVal) + { + Utf8String valueGiven = argv[0].GetValueText(); + if(valueGiven.EqualsIAscii("")) + { + Reset(); + } + else if(!valueGiven.EqualsIAscii(m_text)) + { + m_text = valueGiven; + recompute = true; + } + } + else if(m_ArgType == DbValueType::NullVal) + { + Reset(); + } + else + { + IdSet* valueGiven = (IdSet*)argv[0].GetValuePointer("ID_SET_NAME"); + if(valueGiven != m_virtualSetPtr) + { + m_virtualSetPtr = valueGiven; + recompute = true; + } + } + }else{ + Reset(); + } + if(recompute) + { + m_idSet.clear(); + if(m_ArgType == DbValueType::TextVal && m_text.size() > 0) + { + // Parse String to Js Document and iterate through the array and insert int hex ids as int64 in uniqueIds set. + BeJsDocument doc(m_text.c_str()); + doc.Stringify(StringifyFormat::Indented); + if(!doc.isArray()) + return BE_SQLITE_ERROR; + doc.ForEachArrayMember([&](BeJsValue::ArrayIndex, BeJsConst k1) + { + if(k1.asUInt64(-1) != -1) + m_idSet.insert(BeInt64Id(k1.asUInt64(-1))); + return false; + }); + } + else if(m_virtualSetPtr != nullptr) + { + m_idSet = *m_virtualSetPtr; + } + else + { + return BE_SQLITE_ERROR; + } + } + m_index = m_idSet.begin(); + return BE_SQLITE_OK; +} + +//--------------------------------------------------------------------------------------- +// @bsimethod +//--------------------------------------------------------------------------------------- +void IdSetModule::IdSetTable::IdSetCursor::Reset() { + m_text = ""; + m_virtualSetPtr = nullptr; + m_idSet.clear(); +} +//--------------------------------------------------------------------------------------- +// @bsimethod +//--------------------------------------------------------------------------------------- +DbResult IdSetModule::IdSetTable::BestIndex(IndexInfo& indexInfo) { + int i, j; /* Loop over constraints */ + int idxNum = 0; /* The query plan bitmask */ + int unusableMask = 0; /* Mask of unusable constraints */ + int nArg = 0; /* Number of arguments that seriesFilter() expects */ + int aIdx[2]; /* Constraints on start, stop, and step */ + const int SQLITE_SERIES_CONSTRAINT_VERIFY = 0; + aIdx[0] = aIdx[1] = -1; + int nConstraint = indexInfo.GetConstraintCount(); + + for(i=0; iGetColumn()< 0) continue; + iCol = pConstraint->GetColumn(); + iMask = 1 << iCol; + if (!pConstraint->IsUsable()){ + unusableMask |= iMask; + continue; + } else if (pConstraint->GetOp() == IndexInfo::Operator::EQ ){ + idxNum |= iMask; + aIdx[iCol] = i; + } + } + for( i = 0; i < 2; i++) { + if( (j = aIdx[i]) >= 0 ) { + indexInfo.GetConstraintUsage(j)->SetArgvIndex(++nArg); + indexInfo.GetConstraintUsage(j)->SetOmit(!SQLITE_SERIES_CONSTRAINT_VERIFY); + } + } + + if ((unusableMask & ~idxNum)!=0 ){ + return BE_SQLITE_CONSTRAINT; + } + + indexInfo.SetEstimatedCost(2.0); + indexInfo.SetEstimatedRows(1000); + if( indexInfo.GetIndexOrderByCount() >= 1 && indexInfo.GetOrderBy(0)->GetColumn() == 0 ) { + if( indexInfo.GetOrderBy(0) ->GetDesc()){ + idxNum |= 8; + } else { + idxNum |= 16; + } + indexInfo.SetOrderByConsumed(true); + } + indexInfo.SetIdxNum(idxNum); + return BE_SQLITE_OK; +} + +//--------------------------------------------------------------------------------------- +// @bsimethod +//--------------------------------------------------------------------------------------- +DbResult IdSetModule::Connect(DbVirtualTable*& out, Config& conf, int argc, const char* const* argv) { + out = new IdSetTable(*this); + conf.SetTag(Config::Tags::Innocuous); + return BE_SQLITE_OK; +} + DbResult RegisterBuildInVTabs(ECDbR ecdb) { - auto rc = (new ClassPropsModule(ecdb))->Register(); - return rc; + DbResult rc = (new ClassPropsModule(ecdb))->Register(); + if(rc != BE_SQLITE_OK) + return rc; + rc = (new IdSetModule(ecdb))->Register(); + if(rc != BE_SQLITE_OK) + return rc; + return BE_SQLITE_OK; } END_BENTLEY_SQLITE_EC_NAMESPACE \ No newline at end of file diff --git a/iModelCore/ECDb/ECDb/BuiltInVTabs.h b/iModelCore/ECDb/ECDb/BuiltInVTabs.h index 24e65b2d524..7437b75e1bd 100644 --- a/iModelCore/ECDb/ECDb/BuiltInVTabs.h +++ b/iModelCore/ECDb/ECDb/BuiltInVTabs.h @@ -43,6 +43,61 @@ struct ClassPropsModule : BeSQLite::DbModule { DbResult Connect(DbVirtualTable*& out, Config& conf, int argc, const char* const* argv) final; }; +struct IdSetModule : ECDbModule { + struct IdSetTable : ECDbVirtualTable { + struct IdSetCursor : ECDbCursor { + + private: + Utf8String m_text; + IdSet* m_virtualSetPtr = nullptr; + IdSet m_idSet; + IdSet::const_iterator m_index; + DbValueType m_ArgType; + + public: + IdSetCursor(IdSetTable& vt): ECDbCursor(vt){} + bool Eof() final { return m_index == m_idSet.end() ; } + DbResult Next() final; + DbResult GetColumn(int i, Context& ctx) final; + DbResult GetRowId(int64_t& rowId) final; + DbResult Filter(int idxNum, const char *idxStr, int argc, DbValue* argv) final; + void Reset(); + }; + public: + IdSetTable(IdSetModule& module): ECDbVirtualTable(module) {} + DbResult Open(DbCursor*& cur) override { + cur = new IdSetCursor(*this); + return BE_SQLITE_OK; + } + DbResult BestIndex(IndexInfo& indexInfo) final; + }; + public: + IdSetModule(ECDbR db): ECDbModule( + db, + "IdSet", + "CREATE TABLE x(id hidden)", + R"xml( + + + + + + + + + + + + )xml" + ) {} + DbResult Connect(DbVirtualTable*& out, Config& conf, int argc, const char* const* argv) final; +}; + + DbResult RegisterBuildInVTabs(ECDbR); END_BENTLEY_SQLITE_EC_NAMESPACE \ No newline at end of file diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp index e2419b3f4d5..6cb8bfdc698 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp @@ -89,6 +89,13 @@ std::unique_ptr ECSqlBinderFactory::CreateBinder(ECSqlPrepareContex return CreateVirtualSetBinder(ctx, parameterExp.GetTypeInfo(), paramNameGen); } } + if (const Exp* exp = parameterExp.FindParent(Exp::Type::MemberFunctionCall)) + { + if (MemberFunctionCallExp const* parentExp = exp->GetAsCP()) { + if (parentExp->GetFunctionName().EqualsI("IdSet") && parentExp->GetChildren()[0] == ¶meterExp) + return CreateVirtualSetBinder(ctx, parameterExp.GetTypeInfo(), paramNameGen); + } + } return CreateBinder(ctx, parameterExp.GetTypeInfo(), paramNameGen); } diff --git a/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp b/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp index deb96d264ae..fd5425f4fa4 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp @@ -817,6 +817,21 @@ Exp::FinalizeParseStatus MemberFunctionCallExp::_FinalizeParsing(ECSqlParseConte if (mode == Exp::FinalizeParseMode::AfterFinalizingChildren) { if (this->m_tableValuedFunc) { + if(GetFunctionName().EqualsIAscii("IdSet")) + { + ValueExp const* argExp = GetArgument(0); + if(argExp == nullptr) + return Exp::FinalizeParseStatus::Error; + ECSqlTypeInfo typeInfo = argExp->GetTypeInfo(); + if (!typeInfo.IsPrimitive() && !typeInfo.IsUnset()) + return Exp::FinalizeParseStatus::Error; + if(typeInfo.IsPrimitive()) + { + ECN::PrimitiveType primitiveType = typeInfo.GetPrimitiveType(); + if(primitiveType != ECN::PrimitiveType::PRIMITIVETYPE_String) + return Exp::FinalizeParseStatus::Error; + } + } return FinalizeParseStatus::Completed; } FunctionSignature const* funcSig = FunctionSignatureSet::GetInstance().Find(m_functionName.c_str()); diff --git a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp index 1e872f55aca..4dfe5e30553 100644 --- a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp @@ -3,6 +3,7 @@ * See LICENSE.md in the repository root for full copyright notice. *--------------------------------------------------------------------------------------------*/ #include "ECDbPublishedTests.h" +#include "iostream" USING_NAMESPACE_BENTLEY_EC @@ -16,159 +17,299 @@ struct ECDbIdSetVirtualTableTests : ECDbTestFixture {}; //! Virtual Table to tokenize string // @bsiclass //======================================================================================= -struct IdSetModule : ECDbModule { - struct IdSetTable : ECDbVirtualTable { - struct IdSetCursor : ECDbCursor { +// struct IdSetModule : ECDbModule { +// struct IdSetTable : ECDbVirtualTable { +// struct IdSetCursor : ECDbCursor { - private: - Utf8String m_text; - bset m_idSet; - bset::iterator m_index; - - public: - IdSetCursor(IdSetTable& vt): ECDbCursor(vt){} - bool Eof() final { return m_index == m_idSet.end() ; } - DbResult Next() final { - ++m_index; - return BE_SQLITE_OK; - } - DbResult GetColumn(int i, Context& ctx) final { - ctx.SetResultInt64(*m_index); - return BE_SQLITE_OK; - } - DbResult GetRowId(int64_t& rowId) final { - rowId = *m_index; - return BE_SQLITE_OK; - } - DbResult Filter(int idxNum, const char *idxStr, int argc, DbValue* argv) final { - int i = 0; - if( idxNum & 1 ){ - m_text = argv[i++].GetValueText(); - }else{ - m_text = ""; - } - // Parse String to Js Document and iterate through the array and insert int hex ids as int64 in uniqueIds set. - BeJsDocument doc; - doc.Parse(m_text.c_str()); - doc.ForEachArrayMember([&](BeJsValue::ArrayIndex, BeJsConst k1) - { - m_idSet.insert(k1.asInt64()); - return false; }); - - m_index = m_idSet.begin(); - return BE_SQLITE_OK; - } - }; - public: - IdSetTable(IdSetModule& module): ECDbVirtualTable(module) {} - DbResult Open(DbCursor*& cur) override { - cur = new IdSetCursor(*this); - return BE_SQLITE_OK; - } - DbResult BestIndex(IndexInfo& indexInfo) final { - int i, j; /* Loop over constraints */ - int idxNum = 0; /* The query plan bitmask */ - int unusableMask = 0; /* Mask of unusable constraints */ - int nArg = 0; /* Number of arguments that seriesFilter() expects */ - int aIdx[2]; /* Constraints on start, stop, and step */ - const int SQLITE_SERIES_CONSTRAINT_VERIFY = 0; - aIdx[0] = aIdx[1] = -1; - int nConstraint = indexInfo.GetConstraintCount(); - - for(i=0; iGetColumn()< 0) continue; - iCol = pConstraint->GetColumn(); - iMask = 1 << iCol; - if (!pConstraint->IsUsable()){ - unusableMask |= iMask; - continue; - } else if (pConstraint->GetOp() == IndexInfo::Operator::EQ ){ - idxNum |= iMask; - aIdx[iCol] = i; - } - } - for( i = 0; i < 2; i++) { - if( (j = aIdx[i]) >= 0 ) { - indexInfo.GetConstraintUsage(j)->SetArgvIndex(++nArg); - indexInfo.GetConstraintUsage(j)->SetOmit(!SQLITE_SERIES_CONSTRAINT_VERIFY); - } - } - - if ((unusableMask & ~idxNum)!=0 ){ - return BE_SQLITE_CONSTRAINT; - } - - indexInfo.SetEstimatedCost(2.0); - indexInfo.SetEstimatedRows(1000); - if( indexInfo.GetIndexOrderByCount() >= 1 && indexInfo.GetOrderBy(0)->GetColumn() == 0 ) { - if( indexInfo.GetOrderBy(0) ->GetDesc()){ - idxNum |= 8; - } else { - idxNum |= 16; - } - indexInfo.SetOrderByConsumed(true); - } - indexInfo.SetIdxNum(idxNum); - return BE_SQLITE_OK; - } - }; - public: - IdSetModule(ECDbR db): ECDbModule( - db, - "IdSet", - "CREATE TABLE x(id hidden)", - R"xml( - - - - - - - - - - - - )xml") {} - DbResult Connect(DbVirtualTable*& out, Config& conf, int argc, const char* const* argv) final { - out = new IdSetTable(*this); - conf.SetTag(Config::Tags::Innocuous); - return BE_SQLITE_OK; - } -}; +// private: +// Utf8String m_text; +// IdSet* virtualSetPtr = nullptr; +// bset m_idSet; +// bset::iterator m_index; + +// public: +// IdSetCursor(IdSetTable& vt): ECDbCursor(vt){} +// bool Eof() final { return m_index == m_idSet.end() ; } +// DbResult Next() final { +// ++m_index; +// return BE_SQLITE_OK; +// } +// DbResult GetColumn(int i, Context& ctx) final { +// ctx.SetResultInt64((*m_index).GetValue()); +// return BE_SQLITE_OK; +// } +// DbResult GetRowId(int64_t& rowId) final { +// rowId = (*m_index).GetValue(); +// return BE_SQLITE_OK; +// } +// DbResult Filter(int idxNum, const char *idxStr, int argc, DbValue* argv) final { +// int i = 0; +// int flag = false; +// if( idxNum & 1 ){ +// if(argv[i].GetValueType() == DbValueType::TextVal) +// m_text = argv[i++].GetValueText(); +// else +// virtualSetPtr = (IdSet*)argv[i++].GetValuePointer("ID_SET_NAME"); +// flag = true; +// }else{ +// m_text = ""; +// } +// if(flag ) +// { +// if(m_text.size() > 0) +// { +// // Parse String to Js Document and iterate through the array and insert int hex ids as int64 in uniqueIds set. +// BeJsDocument doc; +// doc.Parse(m_text.c_str()); +// doc.ForEachArrayMember([&](BeJsValue::ArrayIndex, BeJsConst k1) +// { +// m_idSet.insert(BeInt64Id(k1.asUInt64())); +// return false; }); +// } +// else if(virtualSetPtr != nullptr) +// { +// IdSet virtualSet = *virtualSetPtr; +// for(auto it = virtualSet.begin();it!=virtualSet.end();it++) +// { +// m_idSet.insert(*it); +// } +// } +// else +// { +// return BE_SQLITE_ERROR; +// } +// } +// std::cout << m_idSet.size() << std::endl; +// m_index = m_idSet.begin(); +// return BE_SQLITE_OK; +// } +// }; +// public: +// IdSetTable(IdSetModule& module): ECDbVirtualTable(module) {} +// DbResult Open(DbCursor*& cur) override { +// cur = new IdSetCursor(*this); +// return BE_SQLITE_OK; +// } +// DbResult BestIndex(IndexInfo& indexInfo) final { +// int i, j; /* Loop over constraints */ +// int idxNum = 0; /* The query plan bitmask */ +// int unusableMask = 0; /* Mask of unusable constraints */ +// int nArg = 0; /* Number of arguments that seriesFilter() expects */ +// int aIdx[2]; /* Constraints on start, stop, and step */ +// const int SQLITE_SERIES_CONSTRAINT_VERIFY = 0; +// aIdx[0] = aIdx[1] = -1; +// int nConstraint = indexInfo.GetConstraintCount(); + +// for(i=0; iGetColumn()< 0) continue; +// iCol = pConstraint->GetColumn(); +// iMask = 1 << iCol; +// if (!pConstraint->IsUsable()){ +// unusableMask |= iMask; +// continue; +// } else if (pConstraint->GetOp() == IndexInfo::Operator::EQ ){ +// idxNum |= iMask; +// aIdx[iCol] = i; +// } +// } +// for( i = 0; i < 2; i++) { +// if( (j = aIdx[i]) >= 0 ) { +// indexInfo.GetConstraintUsage(j)->SetArgvIndex(++nArg); +// indexInfo.GetConstraintUsage(j)->SetOmit(!SQLITE_SERIES_CONSTRAINT_VERIFY); +// } +// } + +// if ((unusableMask & ~idxNum)!=0 ){ +// return BE_SQLITE_CONSTRAINT; +// } + +// indexInfo.SetEstimatedCost(2.0); +// indexInfo.SetEstimatedRows(1000); +// if( indexInfo.GetIndexOrderByCount() >= 1 && indexInfo.GetOrderBy(0)->GetColumn() == 0 ) { +// if( indexInfo.GetOrderBy(0) ->GetDesc()){ +// idxNum |= 8; +// } else { +// idxNum |= 16; +// } +// indexInfo.SetOrderByConsumed(true); +// } +// indexInfo.SetIdxNum(idxNum); +// return BE_SQLITE_OK; +// } +// }; +// public: +// IdSetModule(ECDbR db): ECDbModule( +// db, +// "IdSet", +// "CREATE TABLE x(id hidden)", +// R"xml( +// +// +// +// +// +// +// +// +// +// +// +// )xml") {} +// DbResult Connect(DbVirtualTable*& out, Config& conf, int argc, const char* const* argv) final { +// out = new IdSetTable(*this); +// conf.SetTag(Config::Tags::Innocuous); +// return BE_SQLITE_OK; +// } +// }; //--------------------------------------------------------------------------------------- // @bsimethod //+---------------+---------------+---------------+---------------+---------------+------ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { ASSERT_EQ(BE_SQLITE_OK, SetupECDb("vtab.ecdb")); - (new IdSetModule(m_ecdb))->Register(); - std::vector hexIds = std::vector{"0x1", "0x2", "0x3", "4", "5"}; - Utf8String jsonArrayString = "["; - int k = 0; - for (k; k < hexIds.size() - 1; k++) { - jsonArrayString += ("\"" + hexIds[k] + "\", "); + std::vector hexIds = std::vector{"0x1", "0x2", "0x3", "4", "5"}; + Utf8String jsonArrayString = "["; + int k = 0; + for (k; k < hexIds.size() - 1; k++) + { + jsonArrayString += ("\"" + hexIds[k] + "\", "); + } + jsonArrayString += ("\"" + hexIds[k] + "\"]"); + + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + stmt.BindText(1, jsonArrayString.c_str(), IECSqlBinder::MakeCopy::No); + + int i = 0; + while (stmt.Step() == BE_SQLITE_ROW) + { + ASSERT_EQ(BeStringUtilities::ParseHex(hexIds[i++].c_str()), stmt.GetValueInt64(0)); + } + ASSERT_EQ(i, hexIds.size()); + } + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + stmt.BindText(1, "[1,2,3,4,5]", IECSqlBinder::MakeCopy::No); + + int i = 0; + while (stmt.Step() == BE_SQLITE_ROW) + { + ASSERT_EQ((1+i++), stmt.GetValueInt64(0)); + } + ASSERT_EQ(i, 5); + } + { + bvector v; + v.push_back(BeInt64Id(1)); + std::shared_ptr> seedIdSet = std::make_shared>(); + for(auto id: v){ + seedIdSet->insert(id); + } + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindVirtualSet(1, seedIdSet)); + while (stmt.Step() == BE_SQLITE_ROW) + { + ASSERT_EQ(1, stmt.GetValueInt64(0)); + } + } + { + bvector v; + v.push_back(BeInt64Id(1)); + std::shared_ptr seedIdSet = std::make_shared(); + for(auto id: v){ + seedIdSet->insert(ECInstanceId(id)); + } + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + ASSERT_EQ(ECSqlStatus::Error, stmt.BindVirtualSet(1, seedIdSet)); + } + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet('[1,2,3,4,5]')"))); + + int i = 0; + while (stmt.Step() == BE_SQLITE_ROW) + { + ASSERT_EQ((1+i++), stmt.GetValueInt64(0)); + } + ASSERT_EQ(i, 5); + } + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet('[\"0x1\",\"0x2\",3,4,5]')"))); + + int i = 0; + while (stmt.Step() == BE_SQLITE_ROW) + { + ASSERT_EQ((1+i), stmt.GetValueInt64(0)); + i++; + } + ASSERT_EQ(i, 5); } - jsonArrayString += ("\"" + hexIds[k] + "\"]"); + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet('[0x1,0x2,\"3\",\"4\",\"5\"]')"))); - ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); - stmt.BindText(1, jsonArrayString.c_str(), IECSqlBinder::MakeCopy::No); + // Should fail while converting to json array because hex values with quotes are required + int i = 0; + while (stmt.Step() == BE_SQLITE_ROW) + { + ASSERT_EQ((1+i), stmt.GetValueInt64(0)); + i++; + } + ASSERT_EQ(i, 0); + } + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + stmt.BindText(1, "[1,\"2\",3, 4.0, 5.0]", IECSqlBinder::MakeCopy::No); - int i = 0; - while (stmt.Step() == BE_SQLITE_ROW) + int i = 0; + while (stmt.Step() == BE_SQLITE_ROW) + { + ASSERT_EQ((1+i), stmt.GetValueInt64(0)); + i++; + } + ASSERT_EQ(i, 5); + } { - ASSERT_EQ(BeStringUtilities::ParseHex(hexIds[i++].c_str()), stmt.GetValueInt64(0)); + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + stmt.BindText(1, "[1,\"2\",3, 4.5, 5.6]", IECSqlBinder::MakeCopy::No); + + // Will not take into account 4.5 and 5.6 because they are decimal values + int i = 0; + while (stmt.Step() == BE_SQLITE_ROW) + { + ASSERT_EQ((1+i), stmt.GetValueInt64(0)); + i++; + } + ASSERT_EQ(i, 3); + } + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + stmt.BindText(1, "[1,\"2\",3, \"Soham\"]", IECSqlBinder::MakeCopy::No); + + // Will not take into account "Soham" because it is a string + int i = 0; + while (stmt.Step() == BE_SQLITE_ROW) + { + ASSERT_EQ((1+i), stmt.GetValueInt64(0)); + i++; + } + ASSERT_EQ(i, 3); } - ASSERT_EQ(i, hexIds.size()); } From 31847a4e6481b671be8361dac879d86f9b4027af Mon Sep 17 00:00:00 2001 From: SohamBhattacharjee Date: Tue, 24 Sep 2024 18:48:24 +0530 Subject: [PATCH 03/43] update --- iModelCore/ECDb/ECDb/BuiltInVTabs.cpp | 104 +++-- iModelCore/ECDb/ECDb/BuiltInVTabs.h | 7 +- .../ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp | 4 +- iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp | 7 - .../ECDb/ECSql/ECSqlPreparedStatement.cpp | 10 +- .../ECDb/ECDb/ECSql/ECSqlPreparedStatement.h | 3 + iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp | 5 + .../NonPublished/ECDbIdSetVirtualTable.cpp | 398 +++++++++++++++--- 8 files changed, 440 insertions(+), 98 deletions(-) diff --git a/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp b/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp index 8c42da9808a..d449383108e 100644 --- a/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp +++ b/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp @@ -165,7 +165,7 @@ DbResult IdSetModule::IdSetTable::IdSetCursor::Next() { // @bsimethod //--------------------------------------------------------------------------------------- DbResult IdSetModule::IdSetTable::IdSetCursor::GetRowId(int64_t& rowId) { - rowId = (*m_index).GetValue(); + rowId = (*m_index); return BE_SQLITE_OK; } @@ -173,7 +173,71 @@ DbResult IdSetModule::IdSetTable::IdSetCursor::GetRowId(int64_t& rowId) { // @bsimethod //--------------------------------------------------------------------------------------- DbResult IdSetModule::IdSetTable::IdSetCursor::GetColumn(int i, Context& ctx) { - ctx.SetResultInt64((*m_index).GetValue()); + ctx.SetResultInt64((*m_index)); + return BE_SQLITE_OK; +} + +//--------------------------------------------------------------------------------------- +// @bsimethod +//--------------------------------------------------------------------------------------- +DbResult IdSetModule::IdSetTable::IdSetCursor::FilterJSONStringIntoArray(BeJsDocument& doc) { + doc.Stringify(StringifyFormat::Indented); + if(!doc.isArray()) + return BE_SQLITE_ERROR; + bool flag = doc.ForEachArrayMember([&](BeJsValue::ArrayIndex, BeJsConst k1) + { + if(BE_SQLITE_OK != FilterJSONBasedOnType(k1)) + return true; + return false; + }); + if(flag) + return BE_SQLITE_ERROR; + return BE_SQLITE_OK; +} + + +//--------------------------------------------------------------------------------------- +// @bsimethod +//--------------------------------------------------------------------------------------- +DbResult IdSetModule::IdSetTable::IdSetCursor::FilterJSONBasedOnType(BeJsConst& val) { + if(val.isNull()) + { + return BE_SQLITE_ERROR; + } + if(val.isNumeric()) + { + if(val.asUInt64(-1) == -1) + return BE_SQLITE_ERROR; + else + m_idSet.insert(val.asUInt64(-1)); + } + else if(val.isArray()) + { + bool flag = val.ForEachArrayMember([&](BeJsValue::ArrayIndex, BeJsConst k1) + { + if(BE_SQLITE_OK != FilterJSONBasedOnType(k1)) + return true; + return false; + }); + if(flag) + return BE_SQLITE_ERROR; + } + else if(val.isString()) + { + if(val.asString().EqualsIAscii("")) + return BE_SQLITE_ERROR; + else if(val.asUInt64(-1) == -1) + { + BeJsDocument doc; + doc.Parse(val.asString()); + if(FilterJSONStringIntoArray(doc) != BE_SQLITE_OK) + return BE_SQLITE_ERROR; + } + else + m_idSet.insert(val.asUInt64(-1)); + } + else + return BE_SQLITE_ERROR; return BE_SQLITE_OK; } @@ -197,19 +261,9 @@ DbResult IdSetModule::IdSetTable::IdSetCursor::Filter(int idxNum, const char *id recompute = true; } } - else if(m_ArgType == DbValueType::NullVal) - { + else{ Reset(); } - else - { - IdSet* valueGiven = (IdSet*)argv[0].GetValuePointer("ID_SET_NAME"); - if(valueGiven != m_virtualSetPtr) - { - m_virtualSetPtr = valueGiven; - recompute = true; - } - } }else{ Reset(); } @@ -218,24 +272,18 @@ DbResult IdSetModule::IdSetTable::IdSetCursor::Filter(int idxNum, const char *id m_idSet.clear(); if(m_ArgType == DbValueType::TextVal && m_text.size() > 0) { - // Parse String to Js Document and iterate through the array and insert int hex ids as int64 in uniqueIds set. - BeJsDocument doc(m_text.c_str()); - doc.Stringify(StringifyFormat::Indented); - if(!doc.isArray()) + BeJsDocument doc; + doc.Parse(m_text.c_str()); + + if(FilterJSONStringIntoArray(doc) != BE_SQLITE_OK) + { + Reset(); return BE_SQLITE_ERROR; - doc.ForEachArrayMember([&](BeJsValue::ArrayIndex, BeJsConst k1) - { - if(k1.asUInt64(-1) != -1) - m_idSet.insert(BeInt64Id(k1.asUInt64(-1))); - return false; - }); - } - else if(m_virtualSetPtr != nullptr) - { - m_idSet = *m_virtualSetPtr; + } } else { + Reset(); return BE_SQLITE_ERROR; } } @@ -248,8 +296,8 @@ DbResult IdSetModule::IdSetTable::IdSetCursor::Filter(int idxNum, const char *id //--------------------------------------------------------------------------------------- void IdSetModule::IdSetTable::IdSetCursor::Reset() { m_text = ""; - m_virtualSetPtr = nullptr; m_idSet.clear(); + m_index = m_idSet.begin(); } //--------------------------------------------------------------------------------------- // @bsimethod diff --git a/iModelCore/ECDb/ECDb/BuiltInVTabs.h b/iModelCore/ECDb/ECDb/BuiltInVTabs.h index 7437b75e1bd..0e423e34222 100644 --- a/iModelCore/ECDb/ECDb/BuiltInVTabs.h +++ b/iModelCore/ECDb/ECDb/BuiltInVTabs.h @@ -49,9 +49,8 @@ struct IdSetModule : ECDbModule { private: Utf8String m_text; - IdSet* m_virtualSetPtr = nullptr; - IdSet m_idSet; - IdSet::const_iterator m_index; + bset m_idSet; + bset::iterator m_index; DbValueType m_ArgType; public: @@ -61,6 +60,8 @@ struct IdSetModule : ECDbModule { DbResult GetColumn(int i, Context& ctx) final; DbResult GetRowId(int64_t& rowId) final; DbResult Filter(int idxNum, const char *idxStr, int argc, DbValue* argv) final; + DbResult FilterJSONBasedOnType(BeJsConst& val); + DbResult FilterJSONStringIntoArray(BeJsDocument& val); void Reset(); }; public: diff --git a/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp index 2f41b731f3b..249d387b271 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp @@ -4,7 +4,7 @@ * See LICENSE.md in the repository root for full copyright notice. *--------------------------------------------------------------------------------------------*/ #include "ECDbPch.h" - +#include "iostream" USING_NAMESPACE_BENTLEY_EC BEGIN_BENTLEY_SQLITE_EC_NAMESPACE @@ -53,8 +53,10 @@ ECSqlStatus ArrayECSqlBinder::_OnBeforeStep() m_json.Accept(writer); Statement& sqliteStmt = GetSqliteStatement(); + std::cout<< sqliteStmt.GetSql() << std::endl; BeAssert(GetMappedSqlParameterNames().size() == 1 && !GetMappedSqlParameterNames()[0].empty()); const int sqlParamIx = sqliteStmt.GetParameterIndex(GetMappedSqlParameterNames()[0].c_str()); + std::cout<< sqlParamIx << std::endl; const DbResult dbRes = sqliteStmt.BindText(sqlParamIx, jsonStr.GetString(), Statement::MakeCopy::Yes); if (BE_SQLITE_OK == dbRes) return ECSqlStatus::Success; diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp index 6cb8bfdc698..e2419b3f4d5 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp @@ -89,13 +89,6 @@ std::unique_ptr ECSqlBinderFactory::CreateBinder(ECSqlPrepareContex return CreateVirtualSetBinder(ctx, parameterExp.GetTypeInfo(), paramNameGen); } } - if (const Exp* exp = parameterExp.FindParent(Exp::Type::MemberFunctionCall)) - { - if (MemberFunctionCallExp const* parentExp = exp->GetAsCP()) { - if (parentExp->GetFunctionName().EqualsI("IdSet") && parentExp->GetChildren()[0] == ¶meterExp) - return CreateVirtualSetBinder(ctx, parameterExp.GetTypeInfo(), paramNameGen); - } - } return CreateBinder(ctx, parameterExp.GetTypeInfo(), paramNameGen); } diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp index b3871ccaf33..d94020c7433 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp @@ -211,8 +211,13 @@ DbResult SingleECSqlPreparedStatement::DoStep() if (SUCCESS != AssertIsValid()) return BE_SQLITE_ERROR; - if (!m_parameterMap.OnBeforeStep().IsSuccess()) - return BE_SQLITE_ERROR; + if(GetOnBeforeFirstStepNotCalled()) + { + if (!m_parameterMap.OnBeforeStep().IsSuccess()) + return BE_SQLITE_ERROR; + SetOnBeforeFirstStepNotCalled(false); + } + const DbResult nativeSqlStatus = m_sqliteStatement.Step(); @@ -264,6 +269,7 @@ ECSqlStatus SingleECSqlPreparedStatement::_Reset() if (nativeSqlStat != BE_SQLITE_OK) return ECSqlStatus(nativeSqlStat); + SetOnBeforeFirstStepNotCalled(true); return ECSqlStatus::Success; } diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h index b355e5f6926..929eb2e7c66 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h @@ -80,6 +80,7 @@ struct SingleECSqlPreparedStatement : IECSqlPreparedStatement private: mutable BeSQLite::Statement m_sqliteStatement; ECSqlParameterMap m_parameterMap; + bool m_onBeforeFirstStepNotCalled = true; IECSqlBinder& _GetBinder(int parameterIndex) const override; int _GetParameterIndex(Utf8CP parameterName) const override; @@ -102,6 +103,8 @@ struct SingleECSqlPreparedStatement : IECSqlPreparedStatement ECSqlParameterMap const& GetParameterMap() const { return m_parameterMap; } ECSqlParameterMap& GetParameterMapR() { return m_parameterMap; } BeSQLite::Statement& GetSqliteStatement() { return m_sqliteStatement; } + bool GetOnBeforeFirstStepNotCalled() { return m_onBeforeFirstStepNotCalled; } + void SetOnBeforeFirstStepNotCalled(bool val) { m_onBeforeFirstStepNotCalled = val; } }; //======================================================================================= diff --git a/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp b/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp index fd5425f4fa4..75fe572310f 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp @@ -930,6 +930,11 @@ Utf8String MemberFunctionCallExp::_ToString() const //+---------------+---------------+---------------+---------------+---------------+------ bool MemberFunctionCallExp::_TryDetermineParameterExpType(ECSqlParseContext& ctx, ParameterExp& parameterExp) const { + if(m_functionName.EqualsIAscii("IdSet")) + { + parameterExp.SetTargetExpInfo(ECSqlTypeInfo::CreatePrimitive(ECN::PRIMITIVETYPE_Long, true)); + return true; + } //we don't have metadata about function args, so use a default type if the arg is a parameter parameterExp.SetTargetExpInfo(ECSqlTypeInfo::CreatePrimitive(ECN::PRIMITIVETYPE_Double)); return true; diff --git a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp index 4dfe5e30553..78d6e6318a4 100644 --- a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp @@ -42,6 +42,119 @@ struct ECDbIdSetVirtualTableTests : ECDbTestFixture {}; // rowId = (*m_index).GetValue(); // return BE_SQLITE_OK; // } +//--------------------------------------------------------------------------------------- +// @bsimethod +//--------------------------------------------------------------------------------------- +// DbResult IdSetModule::IdSetTable::IdSetCursor::FilterRapidJSON(BeJsConst& val) { +// if(val.isNull()) +// { +// return BE_SQLITE_ERROR; +// } +// if(val.isNumeric()) +// { +// if(val.asUInt64(-1) == -1) +// return BE_SQLITE_ERROR; +// else +// { +// uint64_t id = val.asUInt64(-1); +// m_idSet.insert(id); +// } +// } +// else if(val.isArray()) +// { +// bool flag = true; +// val.ForEachArrayMember([&](BeJsValue::ArrayIndex, BeJsConst k1) +// { +// if(BE_SQLITE_OK != FilterRapidJSON(k1)) +// flag = true; +// return false; +// }); +// if(flag) +// return BE_SQLITE_ERROR; +// } +// else if(val.isString()) +// { +// if(val.asUInt64(-1) == -1) +// { +// if(val.asString().EqualsIAscii("")) +// return BE_SQLITE_ERROR; +// BeJsDocument doc; +// doc.Parse(val.asString()); +// if(!doc.isArray()) +// return BE_SQLITE_ERROR; +// bool flag = false; +// doc.ForEachArrayMember([&](BeJsValue::ArrayIndex, BeJsConst k1) +// { +// if(BE_SQLITE_OK != FilterRapidJSON(k1)) +// flag = true; +// return false; +// }); +// if(flag) +// return BE_SQLITE_ERROR; +// } +// else +// m_idSet.insert(val.asUInt64(-1)); +// } +// // switch(val.GetType()) +// // { +// // case rapidjson::Type::kArrayType: +// // { +// // for(rapidjson::Value& v: val.GetArray()) +// // { +// // DbResult res = FilterRapidJSON(v); +// // if(res != BE_SQLITE_OK) +// // return res; +// // } +// // break; +// // } +// // case rapidjson::Type::kNumberType: +// // { +// // if(!val.IsUint64()) +// // return BE_SQLITE_ERROR; +// // int64_t idVal = val.GetUint64(); +// // m_idSet.insert(idVal); +// // break; +// // } +// // case rapidjson::Type::kStringType: +// // { +// // BeJsDocument d; +// // d.Parse(val.GetString()); +// // d.Stringify(StringifyFormat::Indented); +// // bool flag = false; +// // if(d.isArray()) +// // { +// // d.ForEachArrayMember([&](BeJsValue::ArrayIndex, BeJsConst k1) +// // { +// // if(d.asUInt64(-1) == -1) +// // flag = true; +// // else +// // m_idSet.insert(k1.asUInt64()); +// // return false; +// // }); +// // if(flag) +// // return BE_SQLITE_ERROR; +// // } +// // else if(d.isString()) +// // { +// // if(d.asUInt64(-1) == -1) +// // return BE_SQLITE_ERROR; +// // else +// // m_idSet.insert(d.asUInt64()); +// // } +// // else +// // return BE_SQLITE_ERROR; +// // break; +// // } +// // case rapidjson::Type::kNullType: +// // { +// // break; +// // } +// // default: +// // // We don't allow any other types here +// // return BE_SQLITE_ERROR; +// // } +// return BE_SQLITE_OK; +// } // DbResult Filter(int idxNum, const char *idxStr, int argc, DbValue* argv) final { // int i = 0; // int flag = false; @@ -59,13 +172,13 @@ struct ECDbIdSetVirtualTableTests : ECDbTestFixture {}; // if(m_text.size() > 0) // { // // Parse String to Js Document and iterate through the array and insert int hex ids as int64 in uniqueIds set. -// BeJsDocument doc; -// doc.Parse(m_text.c_str()); -// doc.ForEachArrayMember([&](BeJsValue::ArrayIndex, BeJsConst k1) -// { -// m_idSet.insert(BeInt64Id(k1.asUInt64())); -// return false; }); -// } + // BeJsDocument doc; + // doc.Parse(m_text.c_str()); + // doc.ForEachArrayMember([&](BeJsValue::ArrayIndex, BeJsConst k1) + // { + // m_idSet.insert(BeInt64Id(k1.asUInt64())); + // return false; }); + // } // else if(virtualSetPtr != nullptr) // { // IdSet virtualSet = *virtualSetPtr; @@ -175,30 +288,22 @@ struct ECDbIdSetVirtualTableTests : ECDbTestFixture {}; TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { ASSERT_EQ(BE_SQLITE_OK, SetupECDb("vtab.ecdb")); { - std::vector hexIds = std::vector{"0x1", "0x2", "0x3", "4", "5"}; - Utf8String jsonArrayString = "["; - int k = 0; - for (k; k < hexIds.size() - 1; k++) - { - jsonArrayString += ("\"" + hexIds[k] + "\", "); - } - jsonArrayString += ("\"" + hexIds[k] + "\"]"); - ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); - stmt.BindText(1, jsonArrayString.c_str(), IECSqlBinder::MakeCopy::No); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet('[1,2,3,4,5]')"))); int i = 0; while (stmt.Step() == BE_SQLITE_ROW) { - ASSERT_EQ(BeStringUtilities::ParseHex(hexIds[i++].c_str()), stmt.GetValueInt64(0)); + ASSERT_EQ((1+i++), stmt.GetValueInt64(0)); } - ASSERT_EQ(i, hexIds.size()); + ASSERT_EQ(i, 5); } { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); - stmt.BindText(1, "[1,2,3,4,5]", IECSqlBinder::MakeCopy::No); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + IECSqlBinder& elementBinder = arrayBinder.AddArrayElement(); + ASSERT_EQ(ECSqlStatus::Success, elementBinder.BindText("[1,2,3,4,5]", IECSqlBinder::MakeCopy::No)); int i = 0; while (stmt.Step() == BE_SQLITE_ROW) @@ -208,41 +313,22 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { ASSERT_EQ(i, 5); } { - bvector v; - v.push_back(BeInt64Id(1)); - std::shared_ptr> seedIdSet = std::make_shared>(); - for(auto id: v){ - seedIdSet->insert(id); - } + std::vector hexIds = std::vector{"0x1", "0x2", "0x3", "4", "5"}; + ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); - ASSERT_EQ(ECSqlStatus::Success, stmt.BindVirtualSet(1, seedIdSet)); - while (stmt.Step() == BE_SQLITE_ROW) + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + for(int i =0;i v; - v.push_back(BeInt64Id(1)); - std::shared_ptr seedIdSet = std::make_shared(); - for(auto id: v){ - seedIdSet->insert(ECInstanceId(id)); + IECSqlBinder& elementBinder = arrayBinder.AddArrayElement(); + ASSERT_EQ(ECSqlStatus::Success, elementBinder.BindText(hexIds[i].c_str(), IECSqlBinder::MakeCopy::No)); } - ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); - ASSERT_EQ(ECSqlStatus::Error, stmt.BindVirtualSet(1, seedIdSet)); - } - { - ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet('[1,2,3,4,5]')"))); - int i = 0; while (stmt.Step() == BE_SQLITE_ROW) { - ASSERT_EQ((1+i++), stmt.GetValueInt64(0)); + ASSERT_EQ(BeStringUtilities::ParseHex(hexIds[i++].c_str()), stmt.GetValueInt64(0)); } - ASSERT_EQ(i, 5); + ASSERT_EQ(i, hexIds.size()); } { ECSqlStatement stmt; @@ -261,18 +347,69 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet('[0x1,0x2,\"3\",\"4\",\"5\"]')"))); // Should fail while converting to json array because hex values with quotes are required + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); + } + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + IECSqlBinder& elementBinder = arrayBinder.AddArrayElement(); + ASSERT_EQ(ECSqlStatus::Success, elementBinder.BindText( "[1,\"2\",3, 4.0, 5.0]", IECSqlBinder::MakeCopy::No)); + int i = 0; while (stmt.Step() == BE_SQLITE_ROW) { ASSERT_EQ((1+i), stmt.GetValueInt64(0)); i++; } - ASSERT_EQ(i, 0); + ASSERT_EQ(i, 5); + } + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + IECSqlBinder& elementBinder = arrayBinder.AddArrayElement(); + ASSERT_EQ(ECSqlStatus::Success, elementBinder.BindText( "[1,\"2\",3, 4.5, 5.6]", IECSqlBinder::MakeCopy::No)); + + // Will not take into account 4.5 and 5.6 because they are decimal values so should fail + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); + } + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + stmt.BindText(1, "[1,\"2\",3, \"Soham\"]", IECSqlBinder::MakeCopy::No); + + // no binding as we use array ecsql binder so need to call AddArrayElement first + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); } { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); - stmt.BindText(1, "[1,\"2\",3, 4.0, 5.0]", IECSqlBinder::MakeCopy::No); + + // no binding so no data + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); + } + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + IECSqlBinder& elementBinder = arrayBinder.AddArrayElement(); + ASSERT_EQ(ECSqlStatus::Success, elementBinder.BindText( "[1,\"2\",3, \"Soham\"]", IECSqlBinder::MakeCopy::No)); + + // Will not take into account "Soham" because it is a string so should fail + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); + } + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + IECSqlBinder& elementBinder = arrayBinder.AddArrayElement(); + ASSERT_EQ(ECSqlStatus::Success, elementBinder.BindText( "[1,\"2\",3]", IECSqlBinder::MakeCopy::No)); + for(int i = 4;i<=10;i++) + { + IECSqlBinder& elementBinder = arrayBinder.AddArrayElement(); + ASSERT_EQ(ECSqlStatus::Success, elementBinder.BindInt(i)); + } int i = 0; while (stmt.Step() == BE_SQLITE_ROW) @@ -280,35 +417,182 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { ASSERT_EQ((1+i), stmt.GetValueInt64(0)); i++; } - ASSERT_EQ(i, 5); + ASSERT_EQ(i, 10); } { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); - stmt.BindText(1, "[1,\"2\",3, 4.5, 5.6]", IECSqlBinder::MakeCopy::No); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + IECSqlBinder& elementBinder = arrayBinder.AddArrayElement(); + ASSERT_EQ(ECSqlStatus::Success, elementBinder.BindText( "[1,\"2\",3]", IECSqlBinder::MakeCopy::No)); + for(int i = 4;i<=10;i++) + { + IECSqlBinder& elementBinder = arrayBinder.AddArrayElement(); + ASSERT_EQ(ECSqlStatus::Success, elementBinder.BindInt64(i)); + } - // Will not take into account 4.5 and 5.6 because they are decimal values int i = 0; while (stmt.Step() == BE_SQLITE_ROW) { ASSERT_EQ((1+i), stmt.GetValueInt64(0)); i++; } - ASSERT_EQ(i, 3); + ASSERT_EQ(i, 10); } { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); - stmt.BindText(1, "[1,\"2\",3, \"Soham\"]", IECSqlBinder::MakeCopy::No); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + for(int i = 1;i<=10;i++) + { + IECSqlBinder& elementBinder = arrayBinder.AddArrayElement(); + ASSERT_EQ(ECSqlStatus::Success, elementBinder.BindDouble(i)); + } - // Will not take into account "Soham" because it is a string int i = 0; while (stmt.Step() == BE_SQLITE_ROW) { ASSERT_EQ((1+i), stmt.GetValueInt64(0)); i++; } - ASSERT_EQ(i, 3); + ASSERT_EQ(i, 10); + } + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + IECSqlBinder& elementBinder = arrayBinder.AddArrayElement(); + ASSERT_EQ(ECSqlStatus::Success, elementBinder.BindNull()); + for(int i = 1;i<=10;i++) + { + IECSqlBinder& elementBinder = arrayBinder.AddArrayElement(); + ASSERT_EQ(ECSqlStatus::Success, elementBinder.BindDouble(i)); + } + + // having null as an element so should fail + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); + } + { + DPoint2d pArrayOfST1_P2D[] = {DPoint2d::From(-21, 22.1),DPoint2d::From(-85.34, 35.36),DPoint2d::From(-31.34, 12.35)}; + DPoint3d pArrayOfST1_P3D[] = {DPoint3d::From(-12.11, -74.1, 12.3),DPoint3d::From(-12.53, 21.76, -32.22),DPoint3d::From(-41.14, -22.45, -31.16)}; + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + for(int i = 0;i<=2;i++) + { + IECSqlBinder& elementBinder = arrayBinder.AddArrayElement(); + ASSERT_EQ(ECSqlStatus::Error, elementBinder.BindPoint2d(pArrayOfST1_P2D[i])); + } + for(int i = 0;i<=2;i++) + { + IECSqlBinder& elementBinder = arrayBinder.AddArrayElement(); + ASSERT_EQ(ECSqlStatus::Error, elementBinder.BindPoint3d(pArrayOfST1_P3D[i])); + } + + // EmptyArray is Binded + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); + } + { + DPoint2d pArrayOfST1_P2D[] = {DPoint2d::From(-21, 22.1),DPoint2d::From(-85.34, 35.36),DPoint2d::From(-31.34, 12.35)}; + DPoint3d pArrayOfST1_P3D[] = {DPoint3d::From(-12.11, -74.1, 12.3),DPoint3d::From(-12.53, 21.76, -32.22),DPoint3d::From(-41.14, -22.45, -31.16)}; + double pST1P_ST2P_D2 = 431231.3432; + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + for(int i = 0;i<=2;i++) + { + IECSqlBinder& elementBinder = arrayBinder.AddArrayElement(); + ASSERT_EQ(ECSqlStatus::Error, elementBinder["D1"].BindDouble(pST1P_ST2P_D2)); + ASSERT_EQ(ECSqlStatus::Error, elementBinder["P2D"].BindPoint2d(pArrayOfST1_P2D[i])); + } + for(int i = 0;i<=2;i++) + { + IECSqlBinder& elementBinder = arrayBinder.AddArrayElement(); + ASSERT_EQ(ECSqlStatus::Error, elementBinder["D1"].BindDouble(pST1P_ST2P_D2)); + ASSERT_EQ(ECSqlStatus::Error, elementBinder["P3D"].BindPoint3d(pArrayOfST1_P3D[i])); + } + + // EmptyArray is Binded + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); + } + { + const std::vector> bi_array = { + {0x48, 0x65, 0x6}, + {0x48, 0x65, 0x6}, + {0x48, 0x65, 0x6} + }; + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + for(auto& m : bi_array) + ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindBlob((void const*)&m[0], (int)m.size(), IECSqlBinder::MakeCopy::No)); + + // Binary is Binded + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); + } + { + const auto dt = DateTime(DateTime::Kind::Unspecified, 2017, 1, 17, 0, 0); + const auto dtUtc = DateTime(DateTime::Kind::Utc, 2018, 2, 17, 0, 0); + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + for(int i = 0;i<=1;i++) + { + ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindDateTime(dt)); + ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindDateTime(dtUtc)); + } + + // EmptyArray is Binded + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); + } + { + auto geom = IGeometry::Create(ICurvePrimitive::CreateLine(DSegment3d::From(0.0, 0.0, 0.0, 1.0, 1.0, 1.0))); + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + for(int i = 0;i<=1;i++) + { + ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindGeometry(*geom)); + } + + // Binary is Binded because BindGeometry internally calls + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); + } + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + for(int i = 0;i<=1;i++) + { + ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindText("Soham",IECSqlBinder::MakeCopy::No)); + } + + // ["Soham","Soham"] doesnot make sense + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); + } + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + for(int i = 0;i<=1;i++) + { + ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindText("[Soham]",IECSqlBinder::MakeCopy::No)); + } + + // ["[Soham]","[Soham]"] doesnot make sense + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); + } + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + for(int i = 0;i<=1;i++) + { + ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindText("[\"Soham\"]",IECSqlBinder::MakeCopy::No)); + } + + // ["[\"Soham\"]","[\"Soham\"]"] doesnot make sense + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } } From 6ba6b4cf4992e94460c53235657896f731ae305f Mon Sep 17 00:00:00 2001 From: SohamBhattacharjee Date: Wed, 25 Sep 2024 19:08:16 +0530 Subject: [PATCH 04/43] Fixed all the issues related to filtering of data in Virtual Table --- iModelCore/ECDb/ECDb/BuiltInVTabs.cpp | 26 +---- iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp | 32 +++++- iModelCore/ECDb/ECDb/IssueReporter.cpp | 2 + iModelCore/ECDb/ECDb/IssueReporter.h | 2 + .../NonPublished/ECDbIdSetVirtualTable.cpp | 107 ++++++------------ 5 files changed, 69 insertions(+), 100 deletions(-) diff --git a/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp b/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp index d449383108e..577fdcf4fb6 100644 --- a/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp +++ b/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp @@ -181,7 +181,6 @@ DbResult IdSetModule::IdSetTable::IdSetCursor::GetColumn(int i, Context& ctx) { // @bsimethod //--------------------------------------------------------------------------------------- DbResult IdSetModule::IdSetTable::IdSetCursor::FilterJSONStringIntoArray(BeJsDocument& doc) { - doc.Stringify(StringifyFormat::Indented); if(!doc.isArray()) return BE_SQLITE_ERROR; bool flag = doc.ForEachArrayMember([&](BeJsValue::ArrayIndex, BeJsConst k1) @@ -204,35 +203,19 @@ DbResult IdSetModule::IdSetTable::IdSetCursor::FilterJSONBasedOnType(BeJsConst& { return BE_SQLITE_ERROR; } - if(val.isNumeric()) + else if(val.isNumeric()) { if(val.asUInt64(-1) == -1) return BE_SQLITE_ERROR; else m_idSet.insert(val.asUInt64(-1)); } - else if(val.isArray()) - { - bool flag = val.ForEachArrayMember([&](BeJsValue::ArrayIndex, BeJsConst k1) - { - if(BE_SQLITE_OK != FilterJSONBasedOnType(k1)) - return true; - return false; - }); - if(flag) - return BE_SQLITE_ERROR; - } else if(val.isString()) { if(val.asString().EqualsIAscii("")) return BE_SQLITE_ERROR; else if(val.asUInt64(-1) == -1) - { - BeJsDocument doc; - doc.Parse(val.asString()); - if(FilterJSONStringIntoArray(doc) != BE_SQLITE_OK) - return BE_SQLITE_ERROR; - } + return BE_SQLITE_ERROR; else m_idSet.insert(val.asUInt64(-1)); } @@ -264,8 +247,6 @@ DbResult IdSetModule::IdSetTable::IdSetCursor::Filter(int idxNum, const char *id else{ Reset(); } - }else{ - Reset(); } if(recompute) { @@ -278,13 +259,11 @@ DbResult IdSetModule::IdSetTable::IdSetCursor::Filter(int idxNum, const char *id if(FilterJSONStringIntoArray(doc) != BE_SQLITE_OK) { Reset(); - return BE_SQLITE_ERROR; } } else { Reset(); - return BE_SQLITE_ERROR; } } m_index = m_idSet.begin(); @@ -297,7 +276,6 @@ DbResult IdSetModule::IdSetTable::IdSetCursor::Filter(int idxNum, const char *id void IdSetModule::IdSetTable::IdSetCursor::Reset() { m_text = ""; m_idSet.clear(); - m_index = m_idSet.begin(); } //--------------------------------------------------------------------------------------- // @bsimethod diff --git a/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp b/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp index 75fe572310f..5fca09f0c5a 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp @@ -821,15 +821,45 @@ Exp::FinalizeParseStatus MemberFunctionCallExp::_FinalizeParsing(ECSqlParseConte { ValueExp const* argExp = GetArgument(0); if(argExp == nullptr) + { + ctx.Issues().ReportV( + IssueSeverity::Error, + IssueCategory::BusinessProperties, + IssueType::ECSQL, + ECDbIssueId::ECDb_0734, + "There must have an argument for the function'%s'", + m_functionName.c_str() + ); return Exp::FinalizeParseStatus::Error; + } ECSqlTypeInfo typeInfo = argExp->GetTypeInfo(); if (!typeInfo.IsPrimitive() && !typeInfo.IsUnset()) + { + ctx.Issues().ReportV( + IssueSeverity::Error, + IssueCategory::BusinessProperties, + IssueType::ECSQL, + ECDbIssueId::ECDb_0735, + "The argument for the function '%s' can only be either a string literal or an unset parameter", + m_functionName.c_str() + ); return Exp::FinalizeParseStatus::Error; + } if(typeInfo.IsPrimitive()) { ECN::PrimitiveType primitiveType = typeInfo.GetPrimitiveType(); if(primitiveType != ECN::PrimitiveType::PRIMITIVETYPE_String) + { + ctx.Issues().ReportV( + IssueSeverity::Error, + IssueCategory::BusinessProperties, + IssueType::ECSQL, + ECDbIssueId::ECDb_0735, + "The argument for the function '%s' can only be either a string literal or an unset parameter", + m_functionName.c_str() + ); return Exp::FinalizeParseStatus::Error; + } } } return FinalizeParseStatus::Completed; @@ -932,7 +962,7 @@ bool MemberFunctionCallExp::_TryDetermineParameterExpType(ECSqlParseContext& ctx { if(m_functionName.EqualsIAscii("IdSet")) { - parameterExp.SetTargetExpInfo(ECSqlTypeInfo::CreatePrimitive(ECN::PRIMITIVETYPE_Long, true)); + parameterExp.SetTargetExpInfo(ECSqlTypeInfo::CreatePrimitive(ECN::PRIMITIVETYPE_Long, true, EXTENDEDTYPENAME_Id)); return true; } //we don't have metadata about function args, so use a default type if the arg is a parameter diff --git a/iModelCore/ECDb/ECDb/IssueReporter.cpp b/iModelCore/ECDb/ECDb/IssueReporter.cpp index efcb5e2b6d1..ba9c1740f60 100644 --- a/iModelCore/ECDb/ECDb/IssueReporter.cpp +++ b/iModelCore/ECDb/ECDb/IssueReporter.cpp @@ -730,6 +730,8 @@ IssueId ECDbIssueId::ECDb_0730 = IssueId("ECDb_0730"); IssueId ECDbIssueId::ECDb_0731 = IssueId("ECDb_0731"); IssueId ECDbIssueId::ECDb_0732 = IssueId("ECDb_0732"); IssueId ECDbIssueId::ECDb_0733 = IssueId("ECDb_0733"); +IssueId ECDbIssueId::ECDb_0734 = IssueId("ECDb_0734"); +IssueId ECDbIssueId::ECDb_0735 = IssueId("ECDb_0735"); //--------------------------------------------------------------------------------------- // @bsimethod diff --git a/iModelCore/ECDb/ECDb/IssueReporter.h b/iModelCore/ECDb/ECDb/IssueReporter.h index 530fbb8a829..167756b0941 100644 --- a/iModelCore/ECDb/ECDb/IssueReporter.h +++ b/iModelCore/ECDb/ECDb/IssueReporter.h @@ -751,6 +751,8 @@ struct ECDB_EXPORT ECDbIssueId static ECN::IssueId ECDb_0731; static ECN::IssueId ECDb_0732; static ECN::IssueId ECDb_0733; + static ECN::IssueId ECDb_0734; + static ECN::IssueId ECDb_0735; }; //--------------------------------------------------------------------------------------- diff --git a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp index 78d6e6318a4..a49f776412f 100644 --- a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp @@ -298,20 +298,6 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { } ASSERT_EQ(i, 5); } - { - ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); - IECSqlBinder& arrayBinder = stmt.GetBinder(1); - IECSqlBinder& elementBinder = arrayBinder.AddArrayElement(); - ASSERT_EQ(ECSqlStatus::Success, elementBinder.BindText("[1,2,3,4,5]", IECSqlBinder::MakeCopy::No)); - - int i = 0; - while (stmt.Step() == BE_SQLITE_ROW) - { - ASSERT_EQ((1+i++), stmt.GetValueInt64(0)); - } - ASSERT_EQ(i, 5); - } { std::vector hexIds = std::vector{"0x1", "0x2", "0x3", "4", "5"}; @@ -346,16 +332,12 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet('[0x1,0x2,\"3\",\"4\",\"5\"]')"))); - // Should fail while converting to json array because hex values with quotes are required - ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); + // Should fail while converting to json array because hex values with quotes are required so should be empty table + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); } { ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); - IECSqlBinder& arrayBinder = stmt.GetBinder(1); - IECSqlBinder& elementBinder = arrayBinder.AddArrayElement(); - ASSERT_EQ(ECSqlStatus::Success, elementBinder.BindText( "[1,\"2\",3, 4.0, 5.0]", IECSqlBinder::MakeCopy::No)); - + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet('[1,\"2\",3, 4.0, 5.0]')"))); int i = 0; while (stmt.Step() == BE_SQLITE_ROW) { @@ -366,18 +348,15 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { } { ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); - IECSqlBinder& arrayBinder = stmt.GetBinder(1); - IECSqlBinder& elementBinder = arrayBinder.AddArrayElement(); - ASSERT_EQ(ECSqlStatus::Success, elementBinder.BindText( "[1,\"2\",3, 4.5, 5.6]", IECSqlBinder::MakeCopy::No)); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet('[1,\"2\",3, 4.5, 5.6]')"))); - // Will not take into account 4.5 and 5.6 because they are decimal values so should fail - ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); + // Will not take into account 4.5 and 5.6 because they are decimal values so should be empty table + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); } { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); - stmt.BindText(1, "[1,\"2\",3, \"Soham\"]", IECSqlBinder::MakeCopy::No); + ASSERT_EQ(ECSqlStatus::Error,stmt.BindText(1, "[1,\"2\",3, \"abc\"]", IECSqlBinder::MakeCopy::No)); // no binding as we use array ecsql binder so need to call AddArrayElement first ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); @@ -394,18 +373,16 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); IECSqlBinder& arrayBinder = stmt.GetBinder(1); IECSqlBinder& elementBinder = arrayBinder.AddArrayElement(); - ASSERT_EQ(ECSqlStatus::Success, elementBinder.BindText( "[1,\"2\",3, \"Soham\"]", IECSqlBinder::MakeCopy::No)); + ASSERT_EQ(ECSqlStatus::Error, elementBinder.BindText( "[1,\"2\",3, \"abc\"]", IECSqlBinder::MakeCopy::No)); - // Will not take into account "Soham" because it is a string so should fail - ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); + // EmptyArray is Binded so should be empty table + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); } { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); IECSqlBinder& arrayBinder = stmt.GetBinder(1); - IECSqlBinder& elementBinder = arrayBinder.AddArrayElement(); - ASSERT_EQ(ECSqlStatus::Success, elementBinder.BindText( "[1,\"2\",3]", IECSqlBinder::MakeCopy::No)); - for(int i = 4;i<=10;i++) + for(int i = 1;i<=10;i++) { IECSqlBinder& elementBinder = arrayBinder.AddArrayElement(); ASSERT_EQ(ECSqlStatus::Success, elementBinder.BindInt(i)); @@ -419,26 +396,6 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { } ASSERT_EQ(i, 10); } - { - ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); - IECSqlBinder& arrayBinder = stmt.GetBinder(1); - IECSqlBinder& elementBinder = arrayBinder.AddArrayElement(); - ASSERT_EQ(ECSqlStatus::Success, elementBinder.BindText( "[1,\"2\",3]", IECSqlBinder::MakeCopy::No)); - for(int i = 4;i<=10;i++) - { - IECSqlBinder& elementBinder = arrayBinder.AddArrayElement(); - ASSERT_EQ(ECSqlStatus::Success, elementBinder.BindInt64(i)); - } - - int i = 0; - while (stmt.Step() == BE_SQLITE_ROW) - { - ASSERT_EQ((1+i), stmt.GetValueInt64(0)); - i++; - } - ASSERT_EQ(i, 10); - } { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); @@ -469,8 +426,8 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { ASSERT_EQ(ECSqlStatus::Success, elementBinder.BindDouble(i)); } - // having null as an element so should fail - ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); + // having null as an element so should be empty table + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); } { DPoint2d pArrayOfST1_P2D[] = {DPoint2d::From(-21, 22.1),DPoint2d::From(-85.34, 35.36),DPoint2d::From(-31.34, 12.35)}; @@ -489,8 +446,8 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { ASSERT_EQ(ECSqlStatus::Error, elementBinder.BindPoint3d(pArrayOfST1_P3D[i])); } - // EmptyArray is Binded - ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); + // EmptyArray is Binded so should be empty table + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); } { DPoint2d pArrayOfST1_P2D[] = {DPoint2d::From(-21, 22.1),DPoint2d::From(-85.34, 35.36),DPoint2d::From(-31.34, 12.35)}; @@ -512,8 +469,8 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { ASSERT_EQ(ECSqlStatus::Error, elementBinder["P3D"].BindPoint3d(pArrayOfST1_P3D[i])); } - // EmptyArray is Binded - ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); + // EmptyArray is Binded so should be empty table + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); } { const std::vector> bi_array = { @@ -527,8 +484,8 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { for(auto& m : bi_array) ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindBlob((void const*)&m[0], (int)m.size(), IECSqlBinder::MakeCopy::No)); - // Binary is Binded - ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); + // Binary is Binded so should be empty table + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); } { const auto dt = DateTime(DateTime::Kind::Unspecified, 2017, 1, 17, 0, 0); @@ -542,8 +499,8 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindDateTime(dtUtc)); } - // EmptyArray is Binded - ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); + // EmptyArray is Binded so should be empty table + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); } { auto geom = IGeometry::Create(ICurvePrimitive::CreateLine(DSegment3d::From(0.0, 0.0, 0.0, 1.0, 1.0, 1.0))); @@ -555,8 +512,8 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindGeometry(*geom)); } - // Binary is Binded because BindGeometry internally calls - ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); + // Binary is Binded because BindGeometry internally calls so should be empty table + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); } { ECSqlStatement stmt; @@ -564,11 +521,11 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { IECSqlBinder& arrayBinder = stmt.GetBinder(1); for(int i = 0;i<=1;i++) { - ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindText("Soham",IECSqlBinder::MakeCopy::No)); + ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindText("ABC",IECSqlBinder::MakeCopy::No)); } - // ["Soham","Soham"] doesnot make sense - ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); + // EmptyArray is Binded so should be empty table + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); } { ECSqlStatement stmt; @@ -576,11 +533,11 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { IECSqlBinder& arrayBinder = stmt.GetBinder(1); for(int i = 0;i<=1;i++) { - ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindText("[Soham]",IECSqlBinder::MakeCopy::No)); + ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindText("[abc]",IECSqlBinder::MakeCopy::No)); } - // ["[Soham]","[Soham]"] doesnot make sense - ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); + // EmptyArray is Binded so should be empty table + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); } { ECSqlStatement stmt; @@ -588,11 +545,11 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { IECSqlBinder& arrayBinder = stmt.GetBinder(1); for(int i = 0;i<=1;i++) { - ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindText("[\"Soham\"]",IECSqlBinder::MakeCopy::No)); + ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindText("[\"abc\"]",IECSqlBinder::MakeCopy::No)); } - // ["[\"Soham\"]","[\"Soham\"]"] doesnot make sense - ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); + // EmptyArray is Binded so should be empty table + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); } } From c96e44b5f741a43605c77226eab38ea3dafb0fcd Mon Sep 17 00:00:00 2001 From: SohamBhattacharjee Date: Wed, 25 Sep 2024 19:08:38 +0530 Subject: [PATCH 05/43] Binder Type enhancement --- .../ECDb/ECDb/ConcurrentQueryManagerImpl.cpp | 27 ++++++++++++-- .../ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp | 14 ++++++-- iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.h | 3 ++ iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp | 4 +-- iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.h | 8 +++-- .../ECDb/ECDb/ECSql/ECSqlPreparedStatement.h | 10 ++++-- .../ECDb/ECDb/ECSql/ECSqlStatementNoopImpls.h | 5 ++- iModelCore/ECDb/ECDb/ECSql/IECSqlBinder.cpp | 5 +++ iModelCore/ECDb/ECDb/ECSql/IdECSqlBinder.cpp | 2 +- .../ECSql/NavigationPropertyECSqlBinder.cpp | 2 +- iModelCore/ECDb/ECDb/ECSql/PointECSqlBinder.h | 2 +- .../ECDb/ECDb/ECSql/PrimitiveECSqlBinder.h | 2 +- .../ECDb/ECDb/ECSql/StructECSqlBinder.cpp | 2 +- .../ECDb/ECDb/ECSql/VirtualSetBinder.cpp | 2 +- .../ECDb/PublicAPI/ECDb/ECSqlStatement.h | 5 +++ iModelCore/ECDb/PublicAPI/ECDb/IECSqlBinder.h | 36 +++++++++++++++++++ .../NonPublished/ConcurrentQueryTest_V2.cpp | 23 ++++++++++++ 17 files changed, 134 insertions(+), 18 deletions(-) diff --git a/iModelCore/ECDb/ECDb/ConcurrentQueryManagerImpl.cpp b/iModelCore/ECDb/ECDb/ConcurrentQueryManagerImpl.cpp index 53e346509f3..8556ab5ec49 100644 --- a/iModelCore/ECDb/ECDb/ConcurrentQueryManagerImpl.cpp +++ b/iModelCore/ECDb/ECDb/ConcurrentQueryManagerImpl.cpp @@ -1899,8 +1899,31 @@ bool ECSqlParams::TryBindTo(ECSqlStatement& stmt, std::string& err) const { case ECSqlParam::Type::Id: st = stmt.BindId(index, param.GetValueId()); break; case ECSqlParam::Type::IdSet: { - std::shared_ptr> idSet = std::make_shared>(param.GetValueIdSet()); - st = stmt.BindVirtualSet(index, idSet); + if(stmt.GetBinderType(index) == BinderInfo::BinderType::VirtualSetECSqlBinderType) + { + std::shared_ptr> idSet = std::make_shared>(param.GetValueIdSet()); + st = stmt.BindVirtualSet(index, idSet); + } + else if(stmt.GetBinderType(index) == BinderInfo::BinderType::ArrayECSqlBinderType) + { + IECSqlBinder& binder = stmt.GetBinder(index); + IdSet set(param.GetValueIdSet()); + for(auto& ids: set) + { + st = ids.IsValid() ? binder.AddArrayElement().BindInt64((int64_t) ids.GetValue()) : binder.AddArrayElement().BindNull(); + if(st != ECSqlStatus::Success) + { + err = SqlPrintfString("Failed to bind id '%s' while binding IdSet in IdSet VT", ids.ToHexStr().c_str()).GetUtf8CP(); + return false; + } + } + } + else + { + err = "Unsuported binder type for IdSet"; + return false; + } + break; } case ECSqlParam::Type::Integer: diff --git a/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp index 249d387b271..09a894c08ad 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp @@ -13,7 +13,7 @@ BEGIN_BENTLEY_SQLITE_EC_NAMESPACE // @bsimethod //--------------------------------------------------------------------------------------- ArrayECSqlBinder::ArrayECSqlBinder(ECSqlPrepareContext& ctx, ECSqlTypeInfo const& typeInfo, SqlParamNameGenerator& paramNameGen) - : ECSqlBinder(ctx, typeInfo, paramNameGen, 1, true, true) + : ECSqlBinder(ctx, typeInfo, paramNameGen, 1, true, true, BinderInfo::BinderType::ArrayECSqlBinderType) { BeAssert(GetTypeInfo().IsArray()); Initialize(); @@ -70,7 +70,7 @@ ECSqlStatus ArrayECSqlBinder::_OnBeforeStep() // @bsimethod //--------------------------------------------------------------------------------------- ArrayECSqlBinder::JsonValueBinder::JsonValueBinder(ECDbCR ecdb, ECSqlTypeInfo const& typeInfo, rapidjson::Value& json, rapidjson::MemoryPoolAllocator<>& jsonAllocator) - : IECSqlBinder(), m_ecdb(&ecdb), m_typeInfo(typeInfo), m_json(&json), m_jsonAllocator(&jsonAllocator), m_currentArrayElementBinder(nullptr) + : m_ecdb(&ecdb), m_typeInfo(typeInfo), m_json(&json), m_jsonAllocator(&jsonAllocator), m_currentArrayElementBinder(nullptr), m_binderInfo(BinderInfo::BinderType::JsonValueBinderType) { BeAssert(m_json != nullptr); BeAssert(m_jsonAllocator != nullptr); @@ -81,7 +81,7 @@ ArrayECSqlBinder::JsonValueBinder::JsonValueBinder(ECDbCR ecdb, ECSqlTypeInfo co // @bsimethod //--------------------------------------------------------------------------------------- ArrayECSqlBinder::JsonValueBinder::JsonValueBinder(JsonValueBinder&& rhs) - : m_ecdb(std::move(rhs.m_ecdb)), m_typeInfo(std::move(rhs.m_typeInfo)), m_json(std::move(rhs.m_json)), m_jsonAllocator(std::move(rhs.m_jsonAllocator)), m_currentArrayElementBinder(std::move(rhs.m_currentArrayElementBinder)) + : m_ecdb(std::move(rhs.m_ecdb)), m_typeInfo(std::move(rhs.m_typeInfo)), m_json(std::move(rhs.m_json)), m_jsonAllocator(std::move(rhs.m_jsonAllocator)), m_currentArrayElementBinder(std::move(rhs.m_currentArrayElementBinder)), m_binderInfo(std::move(rhs.m_binderInfo)) { if (!rhs.m_structMemberBinders.empty()) m_structMemberBinders = std::move(rhs.m_structMemberBinders); @@ -583,6 +583,14 @@ ECSqlStatus ArrayECSqlBinder::JsonValueBinder::FailIfInvalid() const return ECSqlStatus::Success; } +//--------------------------------------------------------------------------------------- +// @bsimethod +//--------------------------------------------------------------------------------------- +BinderInfo::BinderType ArrayECSqlBinder::JsonValueBinder::_GetBinderType() + { + return m_binderInfo.GetBinderType(); + } + END_BENTLEY_SQLITE_EC_NAMESPACE \ No newline at end of file diff --git a/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.h b/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.h index a17373ee6d7..cb4bb15c0f3 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.h +++ b/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.h @@ -20,6 +20,7 @@ struct ArrayECSqlBinder final : public ECSqlBinder private: ECDb const* m_ecdb = nullptr; ECSqlTypeInfo m_typeInfo; + BinderInfo m_binderInfo; rapidjson::Value* m_json = nullptr; rapidjson::MemoryPoolAllocator<>* m_jsonAllocator = nullptr; @@ -64,6 +65,8 @@ struct ArrayECSqlBinder final : public ECSqlBinder IECSqlBinder& _AddArrayElement() override; + BinderInfo::BinderType _GetBinderType() override; + }; rapidjson::Document m_json; diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp index e2419b3f4d5..b87a6e06c55 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp @@ -10,8 +10,8 @@ BEGIN_BENTLEY_SQLITE_EC_NAMESPACE //--------------------------------------------------------------------------------------- // @bsimethod //--------------------------------------------------------------------------------------- -ECSqlBinder::ECSqlBinder(ECSqlPrepareContext& ctx, ECSqlTypeInfo const& typeInfo, SqlParamNameGenerator& nameGen, int mappedSqlParameterCount, bool hasToCallOnBeforeStep, bool hasToCallOnClearBindings) - : m_preparedStatement(ctx.GetPreparedStatement()), m_typeInfo(typeInfo), m_hasToCallOnBeforeStep(hasToCallOnBeforeStep), m_hasToCallOnClearBindings(hasToCallOnClearBindings) +ECSqlBinder::ECSqlBinder(ECSqlPrepareContext& ctx, ECSqlTypeInfo const& typeInfo, SqlParamNameGenerator& nameGen, int mappedSqlParameterCount, bool hasToCallOnBeforeStep, bool hasToCallOnClearBindings, BinderInfo::BinderType binderType) + : m_preparedStatement(ctx.GetPreparedStatement()), m_typeInfo(typeInfo), m_hasToCallOnBeforeStep(hasToCallOnBeforeStep), m_hasToCallOnClearBindings(hasToCallOnClearBindings), m_binderInfo(binderType) { for (int i = 0; i < mappedSqlParameterCount; i++) { diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.h b/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.h index fe3e94b4582..05144bc71ac 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.h +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.h @@ -58,14 +58,16 @@ struct ECSqlBinder : IECSqlBinder std::vector m_mappedSqlParameterNames; bool m_hasToCallOnBeforeStep = false; bool m_hasToCallOnClearBindings = false; + BinderInfo m_binderInfo; virtual ECSqlStatus _OnBeforeStep() { return ECSqlStatus::Success; } virtual void _OnClearBindings() {} + BinderInfo::BinderType _GetBinderType() override { return m_binderInfo.GetBinderType(); }; protected: - ECSqlBinder(ECSqlPrepareContext&, ECSqlTypeInfo const&, SqlParamNameGenerator&, int mappedSqlParameterCount, bool hasToCallOnBeforeStep, bool hasToCallOnClearBindings); + ECSqlBinder(ECSqlPrepareContext&, ECSqlTypeInfo const&, SqlParamNameGenerator&, int mappedSqlParameterCount, bool hasToCallOnBeforeStep, bool hasToCallOnClearBindings, BinderInfo::BinderType binderType); //! Use this ctor for compound binders where the mapped sql parameter count depends on its member binders - ECSqlBinder(ECSqlPrepareContext& ctx, ECSqlTypeInfo const& typeInfo, SqlParamNameGenerator& paramNameGen, bool hasToCallOnBeforeStep, bool hasToCallOnClearBindings) : ECSqlBinder(ctx, typeInfo, paramNameGen, -1, hasToCallOnBeforeStep, hasToCallOnClearBindings) {} + ECSqlBinder(ECSqlPrepareContext& ctx, ECSqlTypeInfo const& typeInfo, SqlParamNameGenerator& paramNameGen, bool hasToCallOnBeforeStep, bool hasToCallOnClearBindings, BinderInfo::BinderType binderType) : ECSqlBinder(ctx, typeInfo, paramNameGen, -1, hasToCallOnBeforeStep, hasToCallOnClearBindings, binderType) {} void AddChildMemberMappedSqlParameterIndices(ECSqlBinder const& memberBinder) { @@ -87,6 +89,8 @@ struct ECSqlBinder : IECSqlBinder std::vector const& GetMappedSqlParameterNames() const { return m_mappedSqlParameterNames; } + BinderInfo::BinderType GetBinderType() { return _GetBinderType(); } + ECSqlTypeInfo const& GetTypeInfo() const { return m_typeInfo; } ECSqlStatus OnBeforeStep() { return _OnBeforeStep(); } diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h index 929eb2e7c66..6ab3a2402b5 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h @@ -151,6 +151,7 @@ struct CompoundECSqlPreparedStatement : IECSqlPreparedStatement private: IECSqlBinder* m_idBinder = nullptr; bool m_boundValueIsNull = true; + BinderInfo m_binderInfo; ECSqlStatus _BindNull() override { m_boundValueIsNull = true; return GetBinder().BindNull(); } ECSqlStatus _BindInt64(int64_t value) override { m_boundValueIsNull = false; return GetBinder().BindInt64(value); } @@ -171,10 +172,12 @@ struct CompoundECSqlPreparedStatement : IECSqlPreparedStatement IECSqlBinder& _BindStructMember(ECN::ECPropertyId structMemberPropertyId) override; IECSqlBinder& _AddArrayElement() override; + BinderInfo::BinderType _GetBinderType() override { return m_binderInfo.GetBinderType(); } + void _AddBinder(IECSqlBinder& binder) override { BeAssert(m_idBinder == nullptr); m_idBinder = &binder; } public: - ProxyECInstanceIdECSqlBinder() : IProxyECSqlBinder() {} + ProxyECInstanceIdECSqlBinder() : IProxyECSqlBinder(), m_binderInfo(BinderInfo::BinderType::ProxyECInstanceIdECSqlBinderType) {} IECSqlBinder& GetBinder() { BeAssert(m_idBinder != nullptr); return *m_idBinder; } @@ -193,6 +196,7 @@ struct CompoundECSqlPreparedStatement : IECSqlPreparedStatement std::map> m_structMemberProxyBindersById; std::map, CompareIUtf8Ascii> m_structMemberProxyBindersByName; std::unique_ptr m_arrayElementProxyBinder; + BinderInfo m_binderInfo; ECSqlStatus _BindNull() override; ECSqlStatus _BindBoolean(bool value) override; @@ -211,10 +215,12 @@ struct CompoundECSqlPreparedStatement : IECSqlPreparedStatement IECSqlBinder& _BindStructMember(ECN::ECPropertyId structMemberPropertyId) override; IECSqlBinder& _AddArrayElement() override; + BinderInfo::BinderType _GetBinderType() override { return m_binderInfo.GetBinderType(); } + void _AddBinder(IECSqlBinder& binder) override { m_binders.push_back(&binder); } public: - ProxyECSqlBinder() : IProxyECSqlBinder() {} + ProxyECSqlBinder() : IProxyECSqlBinder(),m_binderInfo(BinderInfo::BinderType::ProxyECSqlBinderType) {} }; diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlStatementNoopImpls.h b/iModelCore/ECDb/ECDb/ECSql/ECSqlStatementNoopImpls.h index c1baf41d86c..922e96d0de7 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlStatementNoopImpls.h +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlStatementNoopImpls.h @@ -17,8 +17,9 @@ struct NoopECSqlBinder final : public IECSqlBinder private: ECSqlStatus m_errorStatus; static NoopECSqlBinder* s_singleton; + BinderInfo m_binderInfo; - NoopECSqlBinder() : m_errorStatus(ECSqlStatus::Error) {} + NoopECSqlBinder() : m_errorStatus(ECSqlStatus::Error), m_binderInfo(BinderInfo::BinderType::NoopECSqlBinderType) {} ECSqlStatus _BindNull() override { return m_errorStatus; } ECSqlStatus _BindBoolean(bool value) override { return m_errorStatus; } @@ -39,6 +40,8 @@ struct NoopECSqlBinder final : public IECSqlBinder IECSqlBinder& _AddArrayElement() override { return *this; } + BinderInfo::BinderType _GetBinderType() override { return m_binderInfo.GetBinderType(); } + public: static NoopECSqlBinder& Get(); }; diff --git a/iModelCore/ECDb/ECDb/ECSql/IECSqlBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/IECSqlBinder.cpp index db961ca5cb3..65e13b8fcd5 100644 --- a/iModelCore/ECDb/ECDb/ECSql/IECSqlBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/IECSqlBinder.cpp @@ -152,4 +152,9 @@ IECSqlBinder& IECSqlBinder::operator[](ECN::ECPropertyId structMemberPropertyId) //--------------------------------------------------------------------------------------- IECSqlBinder& IECSqlBinder::AddArrayElement() { return _AddArrayElement(); } +//--------------------------------------------------------------------------------------- +// @bsimethod +//--------------------------------------------------------------------------------------- +BinderInfo::BinderType IECSqlBinder::GetBinderType() { return _GetBinderType(); } + END_BENTLEY_SQLITE_EC_NAMESPACE \ No newline at end of file diff --git a/iModelCore/ECDb/ECDb/ECSql/IdECSqlBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/IdECSqlBinder.cpp index 979171890e6..f1d131bbf29 100644 --- a/iModelCore/ECDb/ECDb/ECSql/IdECSqlBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/IdECSqlBinder.cpp @@ -12,7 +12,7 @@ BEGIN_BENTLEY_SQLITE_EC_NAMESPACE // @bsimethod //--------------------------------------------------------------------------------------- IdECSqlBinder::IdECSqlBinder(ECSqlPrepareContext& ctx, ECSqlTypeInfo const& typeInfo, bool isNoop, SqlParamNameGenerator& paramNameGen) - : ECSqlBinder(ctx, typeInfo, paramNameGen, isNoop ? 0 : 1, false, false), m_isNoop(isNoop) + : ECSqlBinder(ctx, typeInfo, paramNameGen, isNoop ? 0 : 1, false, false,BinderInfo::BinderType::IdECSqlBinderType), m_isNoop(isNoop) {} //--------------------------------------------------------------------------------------- diff --git a/iModelCore/ECDb/ECDb/ECSql/NavigationPropertyECSqlBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/NavigationPropertyECSqlBinder.cpp index 893dc2f49fe..3570f46612c 100644 --- a/iModelCore/ECDb/ECDb/ECSql/NavigationPropertyECSqlBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/NavigationPropertyECSqlBinder.cpp @@ -11,7 +11,7 @@ BEGIN_BENTLEY_SQLITE_EC_NAMESPACE // @bsimethod //--------------------------------------------------------------------------------------- NavigationPropertyECSqlBinder::NavigationPropertyECSqlBinder(ECSqlPrepareContext& ctx, ECSqlTypeInfo const& ecsqlTypeInfo, SqlParamNameGenerator& paramNameGen) - : ECSqlBinder(ctx, ecsqlTypeInfo, paramNameGen, true, false) + : ECSqlBinder(ctx, ecsqlTypeInfo, paramNameGen, true, false, BinderInfo::BinderType::NavigationPropertyECSqlBinderType) { Initialize(ctx, paramNameGen); } diff --git a/iModelCore/ECDb/ECDb/ECSql/PointECSqlBinder.h b/iModelCore/ECDb/ECDb/ECSql/PointECSqlBinder.h index 92335f427a2..a5ed1e18bb9 100644 --- a/iModelCore/ECDb/ECDb/ECSql/PointECSqlBinder.h +++ b/iModelCore/ECDb/ECDb/ECSql/PointECSqlBinder.h @@ -53,7 +53,7 @@ struct PointECSqlBinder final : public ECSqlBinder public: PointECSqlBinder(ECSqlPrepareContext& ctx, ECSqlTypeInfo const& typeInfo, bool isPoint3d, SqlParamNameGenerator& paramNameGen) - : ECSqlBinder(ctx, typeInfo, paramNameGen, isPoint3d ? 3 : 2, false, false), m_isPoint3d(isPoint3d) + : ECSqlBinder(ctx, typeInfo, paramNameGen, isPoint3d ? 3 : 2, false, false, BinderInfo::BinderType::PointECSqlBinderType), m_isPoint3d(isPoint3d) {} ~PointECSqlBinder() {} diff --git a/iModelCore/ECDb/ECDb/ECSql/PrimitiveECSqlBinder.h b/iModelCore/ECDb/ECDb/ECSql/PrimitiveECSqlBinder.h index 8cfaafff215..fc07109aa96 100644 --- a/iModelCore/ECDb/ECDb/ECSql/PrimitiveECSqlBinder.h +++ b/iModelCore/ECDb/ECDb/ECSql/PrimitiveECSqlBinder.h @@ -43,7 +43,7 @@ struct PrimitiveECSqlBinder final : public ECSqlBinder } public: - PrimitiveECSqlBinder(ECSqlPrepareContext& ctx, ECSqlTypeInfo const& typeInfo, SqlParamNameGenerator& paramNameGen) : ECSqlBinder(ctx, typeInfo, paramNameGen, 1, false, false) {} + PrimitiveECSqlBinder(ECSqlPrepareContext& ctx, ECSqlTypeInfo const& typeInfo, SqlParamNameGenerator& paramNameGen) : ECSqlBinder(ctx, typeInfo, paramNameGen, 1, false, false, BinderInfo::BinderType::PrimitiveECSqlBinderType) {} ~PrimitiveECSqlBinder() { OnClearBindings(); } }; diff --git a/iModelCore/ECDb/ECDb/ECSql/StructECSqlBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/StructECSqlBinder.cpp index d384e4e45b6..afc24cf2401 100644 --- a/iModelCore/ECDb/ECDb/ECSql/StructECSqlBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/StructECSqlBinder.cpp @@ -10,7 +10,7 @@ BEGIN_BENTLEY_SQLITE_EC_NAMESPACE // @bsimethod //--------------------------------------------------------------------------------------- StructECSqlBinder::StructECSqlBinder(ECSqlPrepareContext& ctx, ECSqlTypeInfo const& ecsqlTypeInfo, SqlParamNameGenerator& paramNameGen) - : ECSqlBinder(ctx, ecsqlTypeInfo, paramNameGen, true, true) + : ECSqlBinder(ctx, ecsqlTypeInfo, paramNameGen, true, true, BinderInfo::BinderType::StructECSqlBinderType) { Initialize(ctx, paramNameGen); } diff --git a/iModelCore/ECDb/ECDb/ECSql/VirtualSetBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/VirtualSetBinder.cpp index 2730fe5012c..664f44b72f0 100644 --- a/iModelCore/ECDb/ECDb/ECSql/VirtualSetBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/VirtualSetBinder.cpp @@ -12,7 +12,7 @@ BEGIN_BENTLEY_SQLITE_EC_NAMESPACE // @bsimethod //--------------------------------------------------------------------------------------- VirtualSetBinder::VirtualSetBinder(ECSqlPrepareContext& ctx, ECSqlTypeInfo const& typeInfo, SqlParamNameGenerator& paramNameGen) - : ECSqlBinder(ctx, typeInfo, paramNameGen, 1, false, false) + : ECSqlBinder(ctx, typeInfo, paramNameGen, 1, false, false, BinderInfo::BinderType::VirtualSetECSqlBinderType) {} //--------------------------------------------------------------------------------------- diff --git a/iModelCore/ECDb/PublicAPI/ECDb/ECSqlStatement.h b/iModelCore/ECDb/PublicAPI/ECDb/ECSqlStatement.h index c723d36fc78..d38ebce47f3 100644 --- a/iModelCore/ECDb/PublicAPI/ECDb/ECSqlStatement.h +++ b/iModelCore/ECDb/PublicAPI/ECDb/ECSqlStatement.h @@ -249,6 +249,11 @@ struct EXPORT_VTABLE_ATTRIBUTE ECSqlStatement //! @return Parameter binder ECDB_EXPORT IECSqlBinder& GetBinder(int parameterIndex); + //! Gets BinderType for the binder associated with the parameter at the specified index. + //! @param[in] parameterIndex Parameter index. + //! @return Parameter BinderType + ECDB_EXPORT BinderInfo::BinderType GetBinderType(int parameterIndex) { return GetBinder(parameterIndex).GetBinderType(); } + //! Gets the parameter index for a named parameter. Will log an error if parameter not found. //! //! @e Example diff --git a/iModelCore/ECDb/PublicAPI/ECDb/IECSqlBinder.h b/iModelCore/ECDb/PublicAPI/ECDb/IECSqlBinder.h index ae1214f54d2..3c46a644a14 100644 --- a/iModelCore/ECDb/PublicAPI/ECDb/IECSqlBinder.h +++ b/iModelCore/ECDb/PublicAPI/ECDb/IECSqlBinder.h @@ -10,6 +10,35 @@ BEGIN_BENTLEY_SQLITE_EC_NAMESPACE +//======================================================================================= +// @bsiclass +//+===============+===============+===============+===============+===============+====== +struct BinderInfo final + { + public: + enum class BinderType + { + ArrayECSqlBinderType, + IdECSqlBinderType, + VirtualSetECSqlBinderType, + NavigationPropertyECSqlBinderType, + PointECSqlBinderType, + PrimitiveECSqlBinderType, + StructECSqlBinderType, + JsonValueBinderType, + NoopECSqlBinderType, + ProxyECInstanceIdECSqlBinderType, + ProxyECSqlBinderType + }; + + private: + BinderType m_binderType; + public: + BinderInfo(BinderType binderType) : m_binderType(binderType){} + BinderInfo::BinderType GetBinderType() const { return m_binderType; } + }; + + //======================================================================================= //! IECSqlBinder is used to bind a value to a binding parameter in an ECSqlStatement. @@ -56,6 +85,9 @@ struct EXPORT_VTABLE_ATTRIBUTE IECSqlBinder virtual IECSqlBinder& _BindStructMember(ECN::ECPropertyId structMemberPropertyId) = 0; virtual IECSqlBinder& _AddArrayElement() = 0; + virtual BinderInfo::BinderType _GetBinderType() = 0; + + protected: #if !defined (DOCUMENTATION_GENERATOR) //not inlined to prevent being called outside ECDb @@ -197,6 +229,10 @@ struct EXPORT_VTABLE_ATTRIBUTE IECSqlBinder //! Adds a new array element to the array to be bound to the parameter. //! @return The binder for the new array element ECDB_EXPORT IECSqlBinder& AddArrayElement(); + + //! Adds a new array element to the array to be bound to the parameter. + //! @return The binder for the new array element + ECDB_EXPORT BinderInfo::BinderType GetBinderType(); }; END_BENTLEY_SQLITE_EC_NAMESPACE diff --git a/iModelCore/ECDb/Tests/NonPublished/ConcurrentQueryTest_V2.cpp b/iModelCore/ECDb/Tests/NonPublished/ConcurrentQueryTest_V2.cpp index 597693af10d..dd25ea49920 100644 --- a/iModelCore/ECDb/Tests/NonPublished/ConcurrentQueryTest_V2.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/ConcurrentQueryTest_V2.cpp @@ -1055,6 +1055,29 @@ TEST_F(ConcurrentQueryFixture, BlobIO) { } } +//--------------------------------------------------------------------------------------- +// @bsimethod +//+---------------+---------------+---------------+---------------+---------------+------ +TEST_F(ConcurrentQueryFixture, ReaderBindingForIdSetVirtualTable) { + + ASSERT_EQ(DbResult::BE_SQLITE_OK, SetupECDb("ConcurrentQuery_Simple.ecdb")); + + auto& mgr = ConcurrentQueryMgr::GetInstance(m_ecdb); + BeIdSet idSet; + idSet.insert(BeInt64Id(10)); + idSet.insert(BeInt64Id(20)); + idSet.insert(BeInt64Id(30)); + idSet.insert(BeInt64Id(40)); + int vsRowCount = 0; + + ECSqlReader vsReaderIdSet(mgr, "select id from test.IdSet(?)", + ECSqlParams().BindIdSet(1, idSet)); + + while(vsReaderIdSet.Next()) { + vsRowCount++; + } + ASSERT_EQ(vsRowCount,4); +} END_ECDBUNITTESTS_NAMESPACE From 7fab42d33d124a5f76d68d138d2088e4ee300f21 Mon Sep 17 00:00:00 2001 From: SohamBhattacharjee Date: Fri, 27 Sep 2024 10:23:40 +0530 Subject: [PATCH 06/43] trying to fix BestIndex --- iModelCore/ECDb/ECDb/BuiltInFuncs.cpp | 4 +- iModelCore/ECDb/ECDb/BuiltInVTabs.cpp | 43 +- .../ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp | 3 - .../NonPublished/ECDbIdSetVirtualTable.cpp | 476 ++++++++++-------- 4 files changed, 283 insertions(+), 243 deletions(-) diff --git a/iModelCore/ECDb/ECDb/BuiltInFuncs.cpp b/iModelCore/ECDb/ECDb/BuiltInFuncs.cpp index 3ec059148a4..d3936654efb 100644 --- a/iModelCore/ECDb/ECDb/BuiltInFuncs.cpp +++ b/iModelCore/ECDb/ECDb/BuiltInFuncs.cpp @@ -414,7 +414,9 @@ void InstanceOfFunc::_ComputeScalar(Context& ctx, int nArgs, DbValue* args) ctx.SetResultError("ec_instanceof() db is close"); return; } - stmt->BindVirtualSet(1, classIds); + stmt-> + + BindVirtualSet(1, classIds); stmt->BindId(2, curId); ctx.SetResultInt(stmt->Step() == BE_SQLITE_ROW ? 1 : 0); } diff --git a/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp b/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp index 577fdcf4fb6..41492187736 100644 --- a/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp +++ b/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp @@ -3,6 +3,7 @@ * See LICENSE.md in the repository root for full copyright notice. *--------------------------------------------------------------------------------------------*/ #include "ECDbPch.h" +#include "iostream" USING_NAMESPACE_BENTLEY_EC BEGIN_BENTLEY_SQLITE_EC_NAMESPACE @@ -282,12 +283,12 @@ void IdSetModule::IdSetTable::IdSetCursor::Reset() { //--------------------------------------------------------------------------------------- DbResult IdSetModule::IdSetTable::BestIndex(IndexInfo& indexInfo) { int i, j; /* Loop over constraints */ - int idxNum = 0; /* The query plan bitmask */ + int idxMask = 0; /* The query plan bitmask */ int unusableMask = 0; /* Mask of unusable constraints */ - int nArg = 0; /* Number of arguments that seriesFilter() expects */ - int aIdx[2]; /* Constraints on start, stop, and step */ + int nArg = 1; /* Number of arguments that seriesFilter() expects */ + int aIdx[1]; /* Constraints on start, stop, and step */ const int SQLITE_SERIES_CONSTRAINT_VERIFY = 0; - aIdx[0] = aIdx[1] = -1; + aIdx[0] = -1; int nConstraint = indexInfo.GetConstraintCount(); for(i=0; iGetOp() == IndexInfo::Operator::EQ ){ - idxNum |= iMask; + idxMask |= iMask; aIdx[iCol] = i; } } - for( i = 0; i < 2; i++) { - if( (j = aIdx[i]) >= 0 ) { - indexInfo.GetConstraintUsage(j)->SetArgvIndex(++nArg); - indexInfo.GetConstraintUsage(j)->SetOmit(!SQLITE_SERIES_CONSTRAINT_VERIFY); - } - } - if ((unusableMask & ~idxNum)!=0 ){ + if ((unusableMask & ~idxMask)!=0 ){ return BE_SQLITE_CONSTRAINT; } - indexInfo.SetEstimatedCost(2.0); - indexInfo.SetEstimatedRows(1000); - if( indexInfo.GetIndexOrderByCount() >= 1 && indexInfo.GetOrderBy(0)->GetColumn() == 0 ) { - if( indexInfo.GetOrderBy(0) ->GetDesc()){ - idxNum |= 8; - } else { - idxNum |= 16; - } - indexInfo.SetOrderByConsumed(true); - } - indexInfo.SetIdxNum(idxNum); + if( aIdx[0]<0 ){ + /* No JSON input. Leave estimatedCost at the huge value that it was + ** initialized to to discourage the query planner from selecting this + ** plan. */ + indexInfo.SetIdxNum(0); + }else{ + indexInfo.SetEstimatedCost(1.0); + j = aIdx[0]; + indexInfo.GetConstraintUsage(j)->SetArgvIndex(nArg); + indexInfo.GetConstraintUsage(j)->SetOmit(!SQLITE_SERIES_CONSTRAINT_VERIFY); + indexInfo.SetIdxNum(1); /* JSON supplied. Plan 1 */ + } return BE_SQLITE_OK; + } //--------------------------------------------------------------------------------------- diff --git a/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp index 09a894c08ad..4b826d89001 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp @@ -4,7 +4,6 @@ * See LICENSE.md in the repository root for full copyright notice. *--------------------------------------------------------------------------------------------*/ #include "ECDbPch.h" -#include "iostream" USING_NAMESPACE_BENTLEY_EC BEGIN_BENTLEY_SQLITE_EC_NAMESPACE @@ -53,10 +52,8 @@ ECSqlStatus ArrayECSqlBinder::_OnBeforeStep() m_json.Accept(writer); Statement& sqliteStmt = GetSqliteStatement(); - std::cout<< sqliteStmt.GetSql() << std::endl; BeAssert(GetMappedSqlParameterNames().size() == 1 && !GetMappedSqlParameterNames()[0].empty()); const int sqlParamIx = sqliteStmt.GetParameterIndex(GetMappedSqlParameterNames()[0].c_str()); - std::cout<< sqlParamIx << std::endl; const DbResult dbRes = sqliteStmt.BindText(sqlParamIx, jsonStr.GetString(), Statement::MakeCopy::Yes); if (BE_SQLITE_OK == dbRes) return ECSqlStatus::Success; diff --git a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp index a49f776412f..39435bc075b 100644 --- a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp @@ -287,65 +287,109 @@ struct ECDbIdSetVirtualTableTests : ECDbTestFixture {}; //+---------------+---------------+---------------+---------------+---------------+------ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { ASSERT_EQ(BE_SQLITE_OK, SetupECDb("vtab.ecdb")); - { + { ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet('[1,2,3,4,5]')"))); - - int i = 0; + // ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT x, id FROM (with cte(x) as(select ECInstanceId from meta.ECClassDef) select x from cte),test.IdSet('[1,2,3,4,5]')"))); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT x FROM test.IdSet('[1,2,3,4,5]'), (with cte(x) as(select ECInstanceId from meta.ECClassDef) select x from cte) where id < x group by x"))); + int i = 2; while (stmt.Step() == BE_SQLITE_ROW) { - ASSERT_EQ((1+i++), stmt.GetValueInt64(0)); + ASSERT_EQ((i++), stmt.GetValueInt64(0)); } - ASSERT_EQ(i, 5); + ASSERT_EQ(i, 88); } + // { + // ECSqlStatement stmt; + // ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT ECClassId, id FROM meta.ECClassDef,test.IdSet('[1,2,3,4,5]')"))); + // // ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet('[1,2,3,4,5]')"))); + // int i = 0; + // while (stmt.Step() == BE_SQLITE_ROW) + // { + // ASSERT_EQ((1+i++), stmt.GetValueInt64(0)); + // } + // ASSERT_EQ(i, 5); + // } { - std::vector hexIds = std::vector{"0x1", "0x2", "0x3", "4", "5"}; - ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); - IECSqlBinder& arrayBinder = stmt.GetBinder(1); - for(int i =0;i 1"))); - // Should fail while converting to json array because hex values with quotes are required so should be empty table - ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); - } - { - ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet('[1,\"2\",3, 4.0, 5.0]')"))); - int i = 0; + int i = 1; while (stmt.Step() == BE_SQLITE_ROW) { - ASSERT_EQ((1+i), stmt.GetValueInt64(0)); - i++; + ASSERT_EQ((1+i++), stmt.GetValueInt64(0)); } ASSERT_EQ(i, 5); } + // { + // std::vector hexIds = std::vector{"0x1", "0x2", "0x3", "4", "5"}; + + // ECSqlStatement stmt; + // ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + // IECSqlBinder& arrayBinder = stmt.GetBinder(1); + // for(int i =0;i> bi_array = { - {0x48, 0x65, 0x6}, - {0x48, 0x65, 0x6}, - {0x48, 0x65, 0x6} - }; - ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); - IECSqlBinder& arrayBinder = stmt.GetBinder(1); - for(auto& m : bi_array) - ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindBlob((void const*)&m[0], (int)m.size(), IECSqlBinder::MakeCopy::No)); + // // EmptyArray is Binded so should be empty table + // ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); + // } + // { + // const std::vector> bi_array = { + // {0x48, 0x65, 0x6}, + // {0x48, 0x65, 0x6}, + // {0x48, 0x65, 0x6} + // }; + // ECSqlStatement stmt; + // ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + // IECSqlBinder& arrayBinder = stmt.GetBinder(1); + // for(auto& m : bi_array) + // ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindBlob((void const*)&m[0], (int)m.size(), IECSqlBinder::MakeCopy::No)); - // Binary is Binded so should be empty table - ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); - } - { - const auto dt = DateTime(DateTime::Kind::Unspecified, 2017, 1, 17, 0, 0); - const auto dtUtc = DateTime(DateTime::Kind::Utc, 2018, 2, 17, 0, 0); - ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); - IECSqlBinder& arrayBinder = stmt.GetBinder(1); - for(int i = 0;i<=1;i++) - { - ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindDateTime(dt)); - ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindDateTime(dtUtc)); - } + // // Binary is Binded so should be empty table + // ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); + // } + // { + // const auto dt = DateTime(DateTime::Kind::Unspecified, 2017, 1, 17, 0, 0); + // const auto dtUtc = DateTime(DateTime::Kind::Utc, 2018, 2, 17, 0, 0); + // ECSqlStatement stmt; + // ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + // IECSqlBinder& arrayBinder = stmt.GetBinder(1); + // for(int i = 0;i<=1;i++) + // { + // ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindDateTime(dt)); + // ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindDateTime(dtUtc)); + // } - // EmptyArray is Binded so should be empty table - ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); - } - { - auto geom = IGeometry::Create(ICurvePrimitive::CreateLine(DSegment3d::From(0.0, 0.0, 0.0, 1.0, 1.0, 1.0))); - ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); - IECSqlBinder& arrayBinder = stmt.GetBinder(1); - for(int i = 0;i<=1;i++) - { - ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindGeometry(*geom)); - } + // // EmptyArray is Binded so should be empty table + // ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); + // } + // { + // auto geom = IGeometry::Create(ICurvePrimitive::CreateLine(DSegment3d::From(0.0, 0.0, 0.0, 1.0, 1.0, 1.0))); + // ECSqlStatement stmt; + // ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + // IECSqlBinder& arrayBinder = stmt.GetBinder(1); + // for(int i = 0;i<=1;i++) + // { + // ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindGeometry(*geom)); + // } - // Binary is Binded because BindGeometry internally calls so should be empty table - ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); - } - { - ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); - IECSqlBinder& arrayBinder = stmt.GetBinder(1); - for(int i = 0;i<=1;i++) - { - ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindText("ABC",IECSqlBinder::MakeCopy::No)); - } + // // Binary is Binded because BindGeometry internally calls so should be empty table + // ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); + // } + // { + // ECSqlStatement stmt; + // ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + // IECSqlBinder& arrayBinder = stmt.GetBinder(1); + // for(int i = 0;i<=1;i++) + // { + // ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindText("ABC",IECSqlBinder::MakeCopy::No)); + // } - // EmptyArray is Binded so should be empty table - ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); - } - { - ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); - IECSqlBinder& arrayBinder = stmt.GetBinder(1); - for(int i = 0;i<=1;i++) - { - ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindText("[abc]",IECSqlBinder::MakeCopy::No)); - } + // // EmptyArray is Binded so should be empty table + // ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); + // } + // { + // ECSqlStatement stmt; + // ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + // IECSqlBinder& arrayBinder = stmt.GetBinder(1); + // for(int i = 0;i<=1;i++) + // { + // ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindText("[abc]",IECSqlBinder::MakeCopy::No)); + // } - // EmptyArray is Binded so should be empty table - ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); - } - { - ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); - IECSqlBinder& arrayBinder = stmt.GetBinder(1); - for(int i = 0;i<=1;i++) - { - ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindText("[\"abc\"]",IECSqlBinder::MakeCopy::No)); - } + // // EmptyArray is Binded so should be empty table + // ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); + // } + // { + // ECSqlStatement stmt; + // ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + // IECSqlBinder& arrayBinder = stmt.GetBinder(1); + // for(int i = 0;i<=1;i++) + // { + // ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindText("[\"abc\"]",IECSqlBinder::MakeCopy::No)); + // } - // EmptyArray is Binded so should be empty table - ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); - } + // // EmptyArray is Binded so should be empty table + // ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); + // } } From 898b7793678f60a1be7617b9224edb1979fa2bfb Mon Sep 17 00:00:00 2001 From: SohamBhattacharjee Date: Mon, 30 Sep 2024 13:31:07 +0530 Subject: [PATCH 07/43] Fixed the single column issue in VT --- iModelCore/ECDb/ECDb/BuiltInVTabs.cpp | 51 +- iModelCore/ECDb/ECDb/BuiltInVTabs.h | 7 +- .../NonPublished/ECDbIdSetVirtualTable.cpp | 510 +++++++++--------- 3 files changed, 293 insertions(+), 275 deletions(-) diff --git a/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp b/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp index 41492187736..2f6c28e9463 100644 --- a/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp +++ b/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp @@ -166,7 +166,7 @@ DbResult IdSetModule::IdSetTable::IdSetCursor::Next() { // @bsimethod //--------------------------------------------------------------------------------------- DbResult IdSetModule::IdSetTable::IdSetCursor::GetRowId(int64_t& rowId) { - rowId = (*m_index); + rowId = (int64_t)(*m_index); return BE_SQLITE_OK; } @@ -174,7 +174,11 @@ DbResult IdSetModule::IdSetTable::IdSetCursor::GetRowId(int64_t& rowId) { // @bsimethod //--------------------------------------------------------------------------------------- DbResult IdSetModule::IdSetTable::IdSetCursor::GetColumn(int i, Context& ctx) { - ctx.SetResultInt64((*m_index)); + if ((Columns)i == Columns::Json_array_ids) { + ctx.SetResultText(m_text.c_str(), (int)m_text.size(), Context::CopyData::No); + } else if ((Columns)i == Columns::Id && m_index != m_idSet.end()) { + ctx.SetResultInt64((int64_t)(*m_index)); + } return BE_SQLITE_OK; } @@ -249,6 +253,10 @@ DbResult IdSetModule::IdSetTable::IdSetCursor::Filter(int idxNum, const char *id Reset(); } } + else + { + Reset(); + } if(recompute) { m_idSet.clear(); @@ -282,47 +290,44 @@ void IdSetModule::IdSetTable::IdSetCursor::Reset() { // @bsimethod //--------------------------------------------------------------------------------------- DbResult IdSetModule::IdSetTable::BestIndex(IndexInfo& indexInfo) { - int i, j; /* Loop over constraints */ - int idxMask = 0; /* The query plan bitmask */ + int i, j; /* Loop over constraints */ + int idxNum = 0; /* The query plan bitmask */ int unusableMask = 0; /* Mask of unusable constraints */ - int nArg = 1; /* Number of arguments that seriesFilter() expects */ - int aIdx[1]; /* Constraints on start, stop, and step */ + int nArg = 0; /* Number of arguments that seriesFilter() expects */ + int aIdx[2]; /* Constraints on start, stop, and step */ const int SQLITE_SERIES_CONSTRAINT_VERIFY = 0; - aIdx[0] = -1; + aIdx[0] = aIdx[1] = -1; int nConstraint = indexInfo.GetConstraintCount(); for(i=0; iGetColumn()< 0) continue; - iCol = pConstraint->GetColumn(); + if( pConstraint->GetColumn()< (int)IdSetCursor::Columns::Json_array_ids) continue; + iCol = pConstraint->GetColumn() - (int)IdSetCursor::Columns::Json_array_ids; iMask = 1 << iCol; if (!pConstraint->IsUsable()){ unusableMask |= iMask; continue; } else if (pConstraint->GetOp() == IndexInfo::Operator::EQ ){ - idxMask |= iMask; + idxNum |= iMask; aIdx[iCol] = i; } } + for( i = 0; i < 1; i++) { + if( (j = aIdx[i]) >= 0 ) { + indexInfo.GetConstraintUsage(j)->SetArgvIndex(++nArg); + indexInfo.GetConstraintUsage(j)->SetOmit(!SQLITE_SERIES_CONSTRAINT_VERIFY); + } + } - if ((unusableMask & ~idxMask)!=0 ){ + if ((unusableMask & ~idxNum)!=0 ){ return BE_SQLITE_CONSTRAINT; } - if( aIdx[0]<0 ){ - /* No JSON input. Leave estimatedCost at the huge value that it was - ** initialized to to discourage the query planner from selecting this - ** plan. */ - indexInfo.SetIdxNum(0); - }else{ - indexInfo.SetEstimatedCost(1.0); - j = aIdx[0]; - indexInfo.GetConstraintUsage(j)->SetArgvIndex(nArg); - indexInfo.GetConstraintUsage(j)->SetOmit(!SQLITE_SERIES_CONSTRAINT_VERIFY); - indexInfo.SetIdxNum(1); /* JSON supplied. Plan 1 */ - } + indexInfo.SetEstimatedCost(0); + indexInfo.SetEstimatedRows(100); + indexInfo.SetIdxNum(idxNum); return BE_SQLITE_OK; } diff --git a/iModelCore/ECDb/ECDb/BuiltInVTabs.h b/iModelCore/ECDb/ECDb/BuiltInVTabs.h index 0e423e34222..78ce9e03b5b 100644 --- a/iModelCore/ECDb/ECDb/BuiltInVTabs.h +++ b/iModelCore/ECDb/ECDb/BuiltInVTabs.h @@ -46,6 +46,11 @@ struct ClassPropsModule : BeSQLite::DbModule { struct IdSetModule : ECDbModule { struct IdSetTable : ECDbVirtualTable { struct IdSetCursor : ECDbCursor { + + enum class Columns{ + Id = 0, + Json_array_ids = 1, + }; private: Utf8String m_text; @@ -76,7 +81,7 @@ struct IdSetModule : ECDbModule { IdSetModule(ECDbR db): ECDbModule( db, "IdSet", - "CREATE TABLE x(id hidden)", + "CREATE TABLE x(id, json_array_ids hidden)", R"xml( hexIds = std::vector{"0x1", "0x2", "0x3", "4", "5"}; + { + std::vector hexIds = std::vector{"0x1", "0x2", "0x3", "4", "5"}; - // ECSqlStatement stmt; - // ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); - // IECSqlBinder& arrayBinder = stmt.GetBinder(1); - // for(int i =0;i> bi_array = { - // {0x48, 0x65, 0x6}, - // {0x48, 0x65, 0x6}, - // {0x48, 0x65, 0x6} - // }; - // ECSqlStatement stmt; - // ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); - // IECSqlBinder& arrayBinder = stmt.GetBinder(1); - // for(auto& m : bi_array) - // ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindBlob((void const*)&m[0], (int)m.size(), IECSqlBinder::MakeCopy::No)); + // EmptyArray is Binded so should be empty table + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); + } + { + const std::vector> bi_array = { + {0x48, 0x65, 0x6}, + {0x48, 0x65, 0x6}, + {0x48, 0x65, 0x6} + }; + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + for(auto& m : bi_array) + ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindBlob((void const*)&m[0], (int)m.size(), IECSqlBinder::MakeCopy::No)); - // // Binary is Binded so should be empty table - // ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); - // } - // { - // const auto dt = DateTime(DateTime::Kind::Unspecified, 2017, 1, 17, 0, 0); - // const auto dtUtc = DateTime(DateTime::Kind::Utc, 2018, 2, 17, 0, 0); - // ECSqlStatement stmt; - // ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); - // IECSqlBinder& arrayBinder = stmt.GetBinder(1); - // for(int i = 0;i<=1;i++) - // { - // ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindDateTime(dt)); - // ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindDateTime(dtUtc)); - // } + // Binary is Binded so should be empty table + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); + } + { + const auto dt = DateTime(DateTime::Kind::Unspecified, 2017, 1, 17, 0, 0); + const auto dtUtc = DateTime(DateTime::Kind::Utc, 2018, 2, 17, 0, 0); + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + for(int i = 0;i<=1;i++) + { + ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindDateTime(dt)); + ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindDateTime(dtUtc)); + } - // // EmptyArray is Binded so should be empty table - // ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); - // } - // { - // auto geom = IGeometry::Create(ICurvePrimitive::CreateLine(DSegment3d::From(0.0, 0.0, 0.0, 1.0, 1.0, 1.0))); - // ECSqlStatement stmt; - // ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); - // IECSqlBinder& arrayBinder = stmt.GetBinder(1); - // for(int i = 0;i<=1;i++) - // { - // ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindGeometry(*geom)); - // } + // EmptyArray is Binded so should be empty table + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); + } + { + auto geom = IGeometry::Create(ICurvePrimitive::CreateLine(DSegment3d::From(0.0, 0.0, 0.0, 1.0, 1.0, 1.0))); + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + for(int i = 0;i<=1;i++) + { + ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindGeometry(*geom)); + } - // // Binary is Binded because BindGeometry internally calls so should be empty table - // ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); - // } - // { - // ECSqlStatement stmt; - // ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); - // IECSqlBinder& arrayBinder = stmt.GetBinder(1); - // for(int i = 0;i<=1;i++) - // { - // ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindText("ABC",IECSqlBinder::MakeCopy::No)); - // } + // Binary is Binded because BindGeometry internally calls so should be empty table + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); + } + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + for(int i = 0;i<=1;i++) + { + ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindText("ABC",IECSqlBinder::MakeCopy::No)); + } - // // EmptyArray is Binded so should be empty table - // ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); - // } - // { - // ECSqlStatement stmt; - // ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); - // IECSqlBinder& arrayBinder = stmt.GetBinder(1); - // for(int i = 0;i<=1;i++) - // { - // ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindText("[abc]",IECSqlBinder::MakeCopy::No)); - // } + // EmptyArray is Binded so should be empty table + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); + } + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + for(int i = 0;i<=1;i++) + { + ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindText("[abc]",IECSqlBinder::MakeCopy::No)); + } - // // EmptyArray is Binded so should be empty table - // ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); - // } - // { - // ECSqlStatement stmt; - // ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); - // IECSqlBinder& arrayBinder = stmt.GetBinder(1); - // for(int i = 0;i<=1;i++) - // { - // ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindText("[\"abc\"]",IECSqlBinder::MakeCopy::No)); - // } + // EmptyArray is Binded so should be empty table + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); + } + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + for(int i = 0;i<=1;i++) + { + ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindText("[\"abc\"]",IECSqlBinder::MakeCopy::No)); + } - // // EmptyArray is Binded so should be empty table - // ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); - // } + // EmptyArray is Binded so should be empty table + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); + } + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT x FROM test.IdSet('[1,2,3,4,5]'), (with cte(x) as(select ECInstanceId from meta.ECClassDef) select x from cte) where id = x group by x"))); + int i = 1; + while (stmt.Step() == BE_SQLITE_ROW) + { + ASSERT_EQ((i++), stmt.GetValueInt64(0)); + } + ASSERT_EQ(i, 6); + } + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT x FROM test.IdSet('[1,2,3,4,5]'), (with cte(x) as(select ECInstanceId from meta.ECClassDef) select x from cte) where id = x group by x"))); + int i = 1; + while (stmt.Step() == BE_SQLITE_ROW) + { + ASSERT_EQ((i++), stmt.GetValueInt64(0)); + } + ASSERT_EQ(i, 6); + } + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT ECInstanceId FROM test.IdSet('[1,2,3,4,5]'), meta.ECClassDef where ECInstanceId = id group by ECInstanceId"))); + ASSERT_STREQ("7 0 0 SCAN IdSet VIRTUAL TABLE INDEX 1: (null) (null) (null) (null);12 0 0 SEARCH main.ec_Class USING INTEGER PRIMARY KEY (rowid=?) (null) (null) (null) (null);15 0 0 USE TEMP B-TREE FOR GROUP BY (null) (null) (null) (null)", m_ecdb.ExplainQuery(stmt.GetNativeSql(), true).c_str()); + } + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT ECClassId FROM test.IdSet('[1,2,3,4,5]'), meta.ECClassDef where ECClassId = id group by ECClassId"))); + ASSERT_STREQ("7 0 0 SCAN IdSet VIRTUAL TABLE INDEX 1: (null) (null) (null) (null);15 0 0 SCAN main.ec_Class USING COVERING INDEX ix_ec_Class_Name (null) (null) (null) (null);17 0 0 USE TEMP B-TREE FOR GROUP BY (null) (null) (null) (null)", m_ecdb.ExplainQuery(stmt.GetNativeSql(), true).c_str()); + } } From 9cd676e4c4fc84f0d42e8aa2ad716a1d1b836bbb Mon Sep 17 00:00:00 2001 From: SohamBhattacharjee Date: Mon, 30 Sep 2024 19:59:51 +0530 Subject: [PATCH 08/43] Performance Tests Added --- .../Performance/PerformanceIdSetTests.cpp | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 iModelCore/ECDb/Tests/Performance/PerformanceIdSetTests.cpp diff --git a/iModelCore/ECDb/Tests/Performance/PerformanceIdSetTests.cpp b/iModelCore/ECDb/Tests/Performance/PerformanceIdSetTests.cpp new file mode 100644 index 00000000000..b6f6ac4438d --- /dev/null +++ b/iModelCore/ECDb/Tests/Performance/PerformanceIdSetTests.cpp @@ -0,0 +1,114 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Bentley Systems, Incorporated. All rights reserved. +* See LICENSE.md in the repository root for full copyright notice. +*--------------------------------------------------------------------------------------------*/ +#include "PerformanceTests.h" + +USING_NAMESPACE_BENTLEY_EC + +BEGIN_ECDBUNITTESTS_NAMESPACE +struct PerformanceIdSetTests : ECDbTestFixture {}; + +//--------------------------------------------------------------------------------------- +// @bsiclass +//+---------------+---------------+---------------+---------------+---------------+------ +TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet) + { + ASSERT_EQ(BE_SQLITE_OK, SetupECDb("SimpleComparisonBetweenInVirtualSet_and_IdSet.ecdb")); + ECSqlStatement stmt_With_InvirtualSet; + ECSqlStatement stmt_With_IdSet; + std::vector v = {"0x373","0x38F", "0x32", "0x01"}; + IdSet emptyIdset; + for(auto str: v) + { + emptyIdset.insert(BeInt64Id(BeStringUtilities::ParseHex(str.c_str()))); + } + std::shared_ptr> idSetPtr = std::make_shared>(emptyIdset); + ASSERT_EQ(ECSqlStatus::Success, stmt_With_InvirtualSet.Prepare(m_ecdb, "with cnt(x) as (values(1) union select x+1 from cnt where x < 1000000 ) select * from cnt where invirtualset(?, x)")); + ASSERT_EQ(ECSqlStatus::Success, stmt_With_InvirtualSet.BindVirtualSet(1, idSetPtr)); + + ASSERT_EQ(ECSqlStatus::Success, stmt_With_IdSet.Prepare(m_ecdb, "select x from test.IdSet(?), (with cnt(x) as (values(1) union select x+1 from cnt where x < 1000000 ) select * from cnt) where id = x")); + IECSqlBinder& binder = stmt_With_IdSet.GetBinder(1); + for(auto str: v) + { + ASSERT_EQ(ECSqlStatus::Success, binder.AddArrayElement().BindText(str.c_str(), IECSqlBinder::MakeCopy::No)); + } + + auto iterator_set = emptyIdset.begin(); + int i = 0; + StopWatch timer_InVirtual_Set(true); + while(stmt_With_InvirtualSet.Step() == BE_SQLITE_ROW) + { + ASSERT_EQ((*iterator_set).GetValue(), stmt_With_InvirtualSet.GetValueInt64(0)); + ++iterator_set; + i++; + } + timer_InVirtual_Set.Stop(); + LOGTODB(TEST_DETAILS, timer_InVirtual_Set.GetElapsedSeconds(), i); + + iterator_set = emptyIdset.begin(); + i = 0; + StopWatch timer_IdSet(true); + while(stmt_With_InvirtualSet.Step() == BE_SQLITE_ROW) + { + ASSERT_EQ((*iterator_set).GetValue(), stmt_With_InvirtualSet.GetValueInt64(0)); + ++iterator_set; + i++; + } + timer_IdSet.Stop(); + LOGTODB(TEST_DETAILS, timer_IdSet.GetElapsed(), i); + + ASSERT_EQ(true, timer_IdSet.GetElapsedSeconds() emptyIdset; + + ASSERT_EQ(ECSqlStatus::Success, stmt_With_InvirtualSet.Prepare(m_ecdb, "with cnt(x) as (values(1) union select x+1 from cnt where x < 1000000 ) select * from cnt where invirtualset(?, x)")); + ASSERT_EQ(ECSqlStatus::Success, stmt_With_IdSet.Prepare(m_ecdb, "select x from test.IdSet(?), (with cnt(x) as (values(1) union select x+1 from cnt where x < 1000000 ) select * from cnt) where id = x")); + + IECSqlBinder& binder = stmt_With_IdSet.GetBinder(1); + for(int i = 0;i<2000;i++) + { + int randNum = std::rand()%(50000-1 + 1) + 1; + ASSERT_EQ(ECSqlStatus::Success, binder.AddArrayElement().BindInt64(randNum)); + emptyIdset.insert(BeInt64Id(randNum)); + } + std::shared_ptr> idSetPtr = std::make_shared>(emptyIdset); + ASSERT_EQ(ECSqlStatus::Success, stmt_With_InvirtualSet.BindVirtualSet(1, idSetPtr)); + + + auto iterator_set = emptyIdset.begin(); + int i = 0; + StopWatch timer_InVirtual_Set(true); + while(stmt_With_InvirtualSet.Step() == BE_SQLITE_ROW) + { + ASSERT_EQ((*iterator_set).GetValue(), stmt_With_InvirtualSet.GetValueInt64(0)); + ++iterator_set; + i++; + } + timer_InVirtual_Set.Stop(); + LOGTODB(TEST_DETAILS, timer_InVirtual_Set.GetElapsedSeconds(), i); + + iterator_set = emptyIdset.begin(); + i = 0; + StopWatch timer_IdSet(true); + while(stmt_With_InvirtualSet.Step() == BE_SQLITE_ROW) + { + ASSERT_EQ((*iterator_set).GetValue(), stmt_With_InvirtualSet.GetValueInt64(0)); + ++iterator_set; + i++; + } + timer_IdSet.Stop(); + LOGTODB(TEST_DETAILS, timer_IdSet.GetElapsed(), i); + + ASSERT_EQ(true, timer_IdSet.GetElapsedSeconds() Date: Mon, 30 Sep 2024 20:05:13 +0530 Subject: [PATCH 09/43] ValueExp_Issue_Id_Change due to conflicts while merging with main --- iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp | 322 ++++++++++++------------ 1 file changed, 161 insertions(+), 161 deletions(-) diff --git a/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp b/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp index bfee20df3b9..ec7cd96a310 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp @@ -104,14 +104,14 @@ Exp::FinalizeParseStatus IIFExp::_FinalizeParsing(ECSqlParseContext& ctx, Finali auto typeInfo = Then()->GetTypeInfo(); if (typeInfo.IsGeometry() || typeInfo.IsPoint() || typeInfo.IsNavigation() || !(typeInfo.IsPrimitive() || typeInfo.IsNull())) { - ctx.Issues().ReportV( - IssueSeverity::Error, - IssueCategory::BusinessProperties, - IssueType::ECSQL, - ECDbIssueId::ECDb_0579, - "Invalid IIF(cond, trueExpr. falseExpr) expression '%s'. In trueExpr , must evalute to a primitive value (string, datetime, numeric, binary or null)", - ToECSql().c_str() - ); + ctx.Issues().ReportV( + IssueSeverity::Error, + IssueCategory::BusinessProperties, + IssueType::ECSQL, + ECDbIssueId::ECDb_0579, + "Invalid IIF(cond, trueExpr. falseExpr) expression '%s'. In trueExpr , must evalute to a primitive value (string, datetime, numeric, binary or null)", + ToECSql().c_str() + ); return FinalizeParseStatus::Error; } if (typeInfo.GetKind() != ECSqlTypeInfo::Kind::Varies && typeInfo.GetKind() != ECSqlTypeInfo::Kind::Unset && !typeInfo.IsNull()) @@ -122,14 +122,14 @@ Exp::FinalizeParseStatus IIFExp::_FinalizeParsing(ECSqlParseContext& ctx, Finali auto typeInfo = Else()->GetTypeInfo(); if (typeInfo.IsGeometry() || typeInfo.IsPoint() || typeInfo.IsNavigation() || !(typeInfo.IsPrimitive() || typeInfo.IsNull())) { - ctx.Issues().ReportV( - IssueSeverity::Error, - IssueCategory::BusinessProperties, - IssueType::ECSQL, - ECDbIssueId::ECDb_0580, - "Invalid IIF(cond, trueExpr. falseExpr) expression '%s'. In falseExpr , must evalute to a primitive value (string, datetime, numeric, binary or null)", - ToECSql().c_str() - ); + ctx.Issues().ReportV( + IssueSeverity::Error, + IssueCategory::BusinessProperties, + IssueType::ECSQL, + ECDbIssueId::ECDb_0580, + "Invalid IIF(cond, trueExpr. falseExpr) expression '%s'. In falseExpr , must evalute to a primitive value (string, datetime, numeric, binary or null)", + ToECSql().c_str() + ); return FinalizeParseStatus::Error; } if (typeInfo.GetKind() != ECSqlTypeInfo::Kind::Varies && typeInfo.GetKind() != ECSqlTypeInfo::Kind::Unset && !typeInfo.IsNull()) @@ -222,14 +222,14 @@ Exp::FinalizeParseStatus SearchCaseValueExp::_FinalizeParsing(ECSqlParseContext& auto typeInfo = Else()->GetTypeInfo(); if (typeInfo.IsGeometry() || typeInfo.IsPoint() || typeInfo.IsNavigation() || !(typeInfo.IsPrimitive() || typeInfo.IsNull())) { - ctx.Issues().ReportV( - IssueSeverity::Error, - IssueCategory::BusinessProperties, - IssueType::ECSQL, - ECDbIssueId::ECDb_0581, - "Invalid CASE-THEN expression '%s'. In THEN , must evalute to a primitive value (string, datetime, numeric, binary or null)", - ToECSql().c_str() - ); + ctx.Issues().ReportV( + IssueSeverity::Error, + IssueCategory::BusinessProperties, + IssueType::ECSQL, + ECDbIssueId::ECDb_0581, + "Invalid CASE-THEN expression '%s'. In THEN , must evalute to a primitive value (string, datetime, numeric, binary or null)", + ToECSql().c_str() + ); return FinalizeParseStatus::Error; } } @@ -352,14 +352,14 @@ Exp::FinalizeParseStatus SearchedWhenClauseExp::_FinalizeParsing(ECSqlParseConte if (typeInfo.IsGeometry() || typeInfo.IsPoint() || typeInfo.IsNavigation() || !(typeInfo.IsPrimitive() || typeInfo.IsNull())) { - ctx.Issues().ReportV( - IssueSeverity::Error, - IssueCategory::BusinessProperties, - IssueType::ECSQL, - ECDbIssueId::ECDb_0582, - "Invalid CASE-THEN expression '%s'. In THEN , must evalute to a primitive value (string, datetime, numeric, binary or null)", - ToECSql().c_str() - ); + ctx.Issues().ReportV( + IssueSeverity::Error, + IssueCategory::BusinessProperties, + IssueType::ECSQL, + ECDbIssueId::ECDb_0582, + "Invalid CASE-THEN expression '%s'. In THEN , must evalute to a primitive value (string, datetime, numeric, binary or null)", + ToECSql().c_str() + ); return FinalizeParseStatus::Error; } @@ -439,8 +439,8 @@ Exp::FinalizeParseStatus BetweenRangeValueExp::_FinalizeParsing(ECSqlParseContex ECSqlTypeInfo const& typeInfo = operand->GetTypeInfo(); if (!typeInfo.IsPrimitive() || typeInfo.IsGeometry() || typeInfo.IsPoint() || typeInfo.IsNavigation()) { - ctx.Issues().ReportV(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0583, - "Invalid BETWEEN expression '%s'. Operands must be of numeric, date/timestamp, or string type.", ToECSql().c_str()); + ctx.Issues().ReportV(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0583, + "Invalid BETWEEN expression '%s'. Operands must be of numeric, date/timestamp, or string type.", ToECSql().c_str()); return FinalizeParseStatus::Error; } } @@ -530,8 +530,8 @@ Exp::FinalizeParseStatus BinaryValueExp::_FinalizeParsing(ECSqlParseContext& ctx if (!typeInfo.IsPrimitive()) { - ctx.Issues().ReportV(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0584, - "Expecting a primitive value expression as operand. '%s'", ToECSql().c_str()); + ctx.Issues().ReportV(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0584, + "Expecting a primitive value expression as operand. '%s'", ToECSql().c_str()); return FinalizeParseStatus::Error; } @@ -545,14 +545,14 @@ Exp::FinalizeParseStatus BinaryValueExp::_FinalizeParsing(ECSqlParseContext& ctx else */ if (expectedType.IsNumeric() && !typeInfo.IsNumeric()) { - ctx.Issues().ReportV(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0052, - "Expecting a numeric value expression as operand. '%s'", ToECSql().c_str()); + ctx.Issues().ReportV(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0052, + "Expecting a numeric value expression as operand. '%s'", ToECSql().c_str()); return FinalizeParseStatus::Error; } else if (expectedType.GetPrimitiveType() == PRIMITIVETYPE_String && typeInfo.GetPrimitiveType() != PRIMITIVETYPE_String) { - ctx.Issues().ReportV(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0053, - "Expecting value expression of type String as operand. '%s'", ToECSql().c_str()); + ctx.Issues().ReportV(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0053, + "Expecting value expression of type String as operand. '%s'", ToECSql().c_str()); return FinalizeParseStatus::Error; } } @@ -623,8 +623,8 @@ Exp::FinalizeParseStatus CastExp::_FinalizeParsing(ECSqlParseContext& ctx, Final //if operands are parameters set the target exp in those expressions if (GetCastOperand()->IsParameterExp()) { - ctx.Issues().ReportV(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0060, - "Parameters are not supported in a CAST expression ('%s').", ToECSql().c_str()); + ctx.Issues().ReportV(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0060, + "Parameters are not supported in a CAST expression ('%s').", ToECSql().c_str()); return FinalizeParseStatus::Error; } @@ -633,14 +633,14 @@ Exp::FinalizeParseStatus CastExp::_FinalizeParsing(ECSqlParseContext& ctx, Final ECN::PrimitiveType targetType; if (ExpHelper::ToPrimitiveType(targetType, GetCastTargetPrimitiveType()) != SUCCESS) { - ctx.Issues().ReportV( - IssueSeverity::Error, - IssueCategory::BusinessProperties, - IssueType::ECSQL, - ECDbIssueId::ECDb_0267, - "Invalid CAST target type '%s'. Valid target types are the EC primitive types, a fully qualified EC struct type or arrays of those.", - GetCastTargetPrimitiveType().c_str() - ); + ctx.Issues().ReportV( + IssueSeverity::Error, + IssueCategory::BusinessProperties, + IssueType::ECSQL, + ECDbIssueId::ECDb_0267, + "Invalid CAST target type '%s'. Valid target types are the EC primitive types, a fully qualified EC struct type or arrays of those.", + GetCastTargetPrimitiveType().c_str() + ); return FinalizeParseStatus::Error; } @@ -659,15 +659,15 @@ Exp::FinalizeParseStatus CastExp::_FinalizeParsing(ECSqlParseContext& ctx, Final ECClassCP targetClassType = ctx.Schemas().GetClass(m_castTargetSchemaName, GetCastTargetTypeName(), SchemaLookupMode::AutoDetect); if (targetClassType == nullptr) { - ctx.Issues().ReportV(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0269, - "Invalid CAST target type '%s.%s'. The type does not exist.", m_castTargetSchemaName.c_str(), GetCastTargetTypeName().c_str()); + ctx.Issues().ReportV(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0269, + "Invalid CAST target type '%s.%s'. The type does not exist.", m_castTargetSchemaName.c_str(), GetCastTargetTypeName().c_str()); return FinalizeParseStatus::Error; } if (!targetClassType->IsStructClass()) { - ctx.Issues().ReportV(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0270, - "Invalid CAST target type '%s.%s'. Only enumeration and struct types are supported.", m_castTargetSchemaName.c_str(), GetCastTargetTypeName().c_str()); + ctx.Issues().ReportV(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0270, + "Invalid CAST target type '%s.%s'. Only enumeration and struct types are supported.", m_castTargetSchemaName.c_str(), GetCastTargetTypeName().c_str()); return FinalizeParseStatus::Error; } @@ -681,16 +681,16 @@ Exp::FinalizeParseStatus CastExp::_FinalizeParsing(ECSqlParseContext& ctx, Final if (!castOperandTypeInfo.IsPrimitive()) { - ctx.Issues().Report(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0272, - "CAST expects operand to be a primitive value expression or the null constant."); + ctx.Issues().Report(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0272, + "CAST expects operand to be a primitive value expression or the null constant."); return FinalizeParseStatus::Error; } ECSqlTypeInfo const& targetTypeInfo = GetTypeInfo(); if (!targetTypeInfo.IsPrimitive()) { - ctx.Issues().ReportV(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0273, - "Invalid target type in CAST expression '%s'. Non-primitive target type can only be used when casting NULL.", ToECSql().c_str()); + ctx.Issues().ReportV(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0273, + "Invalid target type in CAST expression '%s'. Non-primitive target type can only be used when casting NULL.", ToECSql().c_str()); return FinalizeParseStatus::Error; } @@ -702,15 +702,15 @@ Exp::FinalizeParseStatus CastExp::_FinalizeParsing(ECSqlParseContext& ctx, Final { if (typeInfo->IsPoint()) { - ctx.Issues().ReportV( - IssueSeverity::Error, - IssueCategory::BusinessProperties, - IssueType::ECSQL, - ECDbIssueId::ECDb_0274, - "Casting from '%s' to '%s' is not supported", - ExpHelper::ToString(castOperandTypeInfo.GetPrimitiveType()), - ExpHelper::ToString(targetTypeInfo.GetPrimitiveType()) - ); + ctx.Issues().ReportV( + IssueSeverity::Error, + IssueCategory::BusinessProperties, + IssueType::ECSQL, + ECDbIssueId::ECDb_0274, + "Casting from '%s' to '%s' is not supported", + ExpHelper::ToString(castOperandTypeInfo.GetPrimitiveType()), + ExpHelper::ToString(targetTypeInfo.GetPrimitiveType()) + ); return FinalizeParseStatus::Error; } } @@ -826,7 +826,7 @@ Exp::FinalizeParseStatus MemberFunctionCallExp::_FinalizeParsing(ECSqlParseConte IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, - ECDbIssueId::ECDb_0734, + ECDbIssueId::ECDb_0735, "There must have an argument for the function'%s'", m_functionName.c_str() ); @@ -839,7 +839,7 @@ Exp::FinalizeParseStatus MemberFunctionCallExp::_FinalizeParsing(ECSqlParseConte IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, - ECDbIssueId::ECDb_0735, + ECDbIssueId::ECDb_0736, "The argument for the function '%s' can only be either a string literal or an unset parameter", m_functionName.c_str() ); @@ -854,7 +854,7 @@ Exp::FinalizeParseStatus MemberFunctionCallExp::_FinalizeParsing(ECSqlParseConte IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, - ECDbIssueId::ECDb_0735, + ECDbIssueId::ECDb_0736, "The argument for the function '%s' can only be either a string literal or an unset parameter", m_functionName.c_str() ); @@ -867,42 +867,42 @@ Exp::FinalizeParseStatus MemberFunctionCallExp::_FinalizeParsing(ECSqlParseConte FunctionSignature const* funcSig = FunctionSignatureSet::GetInstance().Find(m_functionName.c_str()); if (funcSig == nullptr) { - ctx.Issues().ReportV( - IssueSeverity::Error, - IssueCategory::BusinessProperties, - IssueType::ECSQL, - ECDbIssueId::ECDb_0383, - "Unknown member function '%s'", - m_functionName.c_str() - ); + ctx.Issues().ReportV( + IssueSeverity::Error, + IssueCategory::BusinessProperties, + IssueType::ECSQL, + ECDbIssueId::ECDb_0383, + "Unknown member function '%s'", + m_functionName.c_str() + ); return Exp::FinalizeParseStatus::Error; } if (funcSig->SetParameterType(GetChildrenR()) != SUCCESS) { - ctx.Issues().ReportV( - IssueSeverity::Error, - IssueCategory::BusinessProperties, - IssueType::ECSQL, - ECDbIssueId::ECDb_0403, - "Error in function call '%s' - Varying argument list cannot be parameterized.", - m_functionName.c_str() - ); + ctx.Issues().ReportV( + IssueSeverity::Error, + IssueCategory::BusinessProperties, + IssueType::ECSQL, + ECDbIssueId::ECDb_0403, + "Error in function call '%s' - Varying argument list cannot be parameterized.", + m_functionName.c_str() + ); return Exp::FinalizeParseStatus::Error; } Utf8String err; if (funcSig->Verify(err, GetChildren()) != SUCCESS) { - ctx.Issues().ReportV( - IssueSeverity::Error, - IssueCategory::BusinessProperties, - IssueType::ECSQL, - ECDbIssueId::ECDb_0443, - "Error in function call '%s' - %s", - m_functionName.c_str(), - err.c_str() - ); + ctx.Issues().ReportV( + IssueSeverity::Error, + IssueCategory::BusinessProperties, + IssueType::ECSQL, + ECDbIssueId::ECDb_0443, + "Error in function call '%s' - %s", + m_functionName.c_str(), + err.c_str() + ); return Exp::FinalizeParseStatus::Error; } @@ -1020,28 +1020,28 @@ Exp::FinalizeParseStatus FunctionCallExp::_FinalizeParsing(ECSqlParseContext& ct const size_t argCount = GetChildrenCount(); if (m_setQuantifier != SqlSetQuantifier::NotSpecified && argCount != 1) { - ctx.Issues().ReportV( - IssueSeverity::Error, - IssueCategory::BusinessProperties, - IssueType::ECSQL, - ECDbIssueId::ECDb_0450, - "Function '%s' can only have one argument if used with the %s operator.", - m_functionName.c_str(), - ExpHelper::ToSql(m_setQuantifier) - ); + ctx.Issues().ReportV( + IssueSeverity::Error, + IssueCategory::BusinessProperties, + IssueType::ECSQL, + ECDbIssueId::ECDb_0450, + "Function '%s' can only have one argument if used with the %s operator.", + m_functionName.c_str(), + ExpHelper::ToSql(m_setQuantifier) + ); return FinalizeParseStatus::Error; } if (m_isGetter && argCount != 0) { - ctx.Issues().ReportV( - IssueSeverity::Error, - IssueCategory::BusinessProperties, - IssueType::ECSQL, - ECDbIssueId::ECDb_0477, - "Function '%s' is a getter function any may not have any arguments.", - m_functionName.c_str() - ); + ctx.Issues().ReportV( + IssueSeverity::Error, + IssueCategory::BusinessProperties, + IssueType::ECSQL, + ECDbIssueId::ECDb_0477, + "Function '%s' is a getter function any may not have any arguments.", + m_functionName.c_str() + ); return FinalizeParseStatus::Error; } @@ -1056,14 +1056,14 @@ Exp::FinalizeParseStatus FunctionCallExp::_FinalizeParsing(ECSqlParseContext& ct ECSqlTypeInfo::Kind typeKind = argExp->GetTypeInfo().GetKind(); if (typeKind != ECSqlTypeInfo::Kind::Unset) { - ctx.Issues().ReportV( - IssueSeverity::Error, - IssueCategory::BusinessProperties, - IssueType::ECSQL, - ECDbIssueId::ECDb_0544, - "The function '%s' can only be called with an unset argument type as the first argument, for example, InVirtualSet(?, ). However, the first argument is not unset.", - m_functionName.c_str() - ); + ctx.Issues().ReportV( + IssueSeverity::Error, + IssueCategory::BusinessProperties, + IssueType::ECSQL, + ECDbIssueId::ECDb_0544, + "The function '%s' can only be called with an unset argument type as the first argument, for example, InVirtualSet(?, ). However, the first argument is not unset.", + m_functionName.c_str() + ); return FinalizeParseStatus::Error; } @@ -1079,15 +1079,15 @@ Exp::FinalizeParseStatus FunctionCallExp::_FinalizeParsing(ECSqlParseContext& ct ECSqlTypeInfo::Kind typeKind = argExp->GetTypeInfo().GetKind(); if (typeKind != ECSqlTypeInfo::Kind::Primitive && typeKind != ECSqlTypeInfo::Kind::Null) { - ctx.Issues().ReportV( - IssueSeverity::Error, - IssueCategory::BusinessProperties, - IssueType::ECSQL, - ECDbIssueId::ECDb_0547, - "Function '%s' can only be called with primitive arguments. Argument #%" PRIu64 " is not primitive.", - m_functionName.c_str(), - (uint64_t) (i + 1) - ); + ctx.Issues().ReportV( + IssueSeverity::Error, + IssueCategory::BusinessProperties, + IssueType::ECSQL, + ECDbIssueId::ECDb_0547, + "Function '%s' can only be called with primitive arguments. Argument #%" PRIu64 " is not primitive.", + m_functionName.c_str(), + (uint64_t) (i + 1) + ); return FinalizeParseStatus::Error; } } @@ -1461,8 +1461,8 @@ BentleyStatus LiteralValueExp::Create(std::unique_ptr& exp, ECSqlParse DateTime dt; if (SUCCESS != DateTime::FromString(dt, value)) { - ctx.Issues().ReportV(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0555, - "Invalid format for DATE/TIMESTAMP/TIME in expression '%s'.", valueExp->ToECSql().c_str()); + ctx.Issues().ReportV(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0555, + "Invalid format for DATE/TIMESTAMP/TIME in expression '%s'.", valueExp->ToECSql().c_str()); return ERROR; } @@ -1488,8 +1488,8 @@ BentleyStatus LiteralValueExp::ResolveDataType(ECSqlParseContext& ctx) DateTime dt; if (SUCCESS != DateTime::FromString(dt, m_rawValue.c_str())) { - ctx.Issues().ReportV(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0555, - "Invalid format for DATE/TIMESTAMP/TIME in expression '%s'.", ToECSql().c_str()); + ctx.Issues().ReportV(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0555, + "Invalid format for DATE/TIMESTAMP/TIME in expression '%s'.", ToECSql().c_str()); return ERROR; } @@ -1826,24 +1826,24 @@ Exp::FinalizeParseStatus UnaryValueExp::_FinalizeParsing(ECSqlParseContext& ctx, { switch (m_op) { - case Operator::Minus: - case Operator::Plus: - SetTypeInfo(ECSqlTypeInfo::CreatePrimitive(ECN::PRIMITIVETYPE_Double)); - break; - case Operator::BitwiseNot: - SetTypeInfo(ECSqlTypeInfo::CreatePrimitive(ECN::PRIMITIVETYPE_Long)); - break; - - default: - ctx.Issues().ReportV( - IssueSeverity::Error, - IssueCategory::BusinessProperties, - IssueType::ECSQL, - ECDbIssueId::ECDb_0556, - "Invalid unary operator in expression %s.", - ToECSql().c_str() - ); - return FinalizeParseStatus::Error; + case Operator::Minus: + case Operator::Plus: + SetTypeInfo(ECSqlTypeInfo::CreatePrimitive(ECN::PRIMITIVETYPE_Double)); + break; + case Operator::BitwiseNot: + SetTypeInfo(ECSqlTypeInfo::CreatePrimitive(ECN::PRIMITIVETYPE_Long)); + break; + + default: + ctx.Issues().ReportV( + IssueSeverity::Error, + IssueCategory::BusinessProperties, + IssueType::ECSQL, + ECDbIssueId::ECDb_0556, + "Invalid unary operator in expression %s.", + ToECSql().c_str() + ); + return FinalizeParseStatus::Error; } return FinalizeParseStatus::NotCompleted; @@ -1857,34 +1857,34 @@ Exp::FinalizeParseStatus UnaryValueExp::_FinalizeParsing(ECSqlParseContext& ctx, auto const& operandTypeInfo = operand->GetTypeInfo(); switch (m_op) { - case Operator::Minus: - case Operator::Plus: + case Operator::Minus: + case Operator::Plus: { if (!operandTypeInfo.IsNumeric()) { - ctx.Issues().ReportV(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0557, - "Invalid type in expression %s: Unary operator expects a numeric type expression", ToECSql().c_str()); + ctx.Issues().ReportV(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0557, + "Invalid type in expression %s: Unary operator expects a numeric type expression", ToECSql().c_str()); return FinalizeParseStatus::Error; } break; } - case Operator::BitwiseNot: + case Operator::BitwiseNot: { if (!operandTypeInfo.IsExactNumeric()) { - ctx.Issues().ReportV(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0558, - "Invalid type in expression %s: Unary operator expects an integral type expression", ToECSql().c_str()); + ctx.Issues().ReportV(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0558, + "Invalid type in expression %s: Unary operator expects an integral type expression", ToECSql().c_str()); return FinalizeParseStatus::Error; } break; } - default: + default: { - ctx.Issues().ReportV(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0556, - "Invalid unary operator in expression %s.", ToECSql().c_str()); + ctx.Issues().ReportV(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0556, + "Invalid unary operator in expression %s.", ToECSql().c_str()); return FinalizeParseStatus::Error; } } From 83ef505df91f7e75bfd69cd568ef353cabb8cb04 Mon Sep 17 00:00:00 2001 From: SohamBhattacharjee Date: Thu, 3 Oct 2024 12:43:24 +0530 Subject: [PATCH 10/43] Added flag for binder info to check whether the binder is for a parameter inside InVirtualSet() or IdSet()....useful for BindIdSet() --- .../ECDb/ECDb/ConcurrentQueryManagerImpl.cpp | 4 +-- .../ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp | 4 +-- iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.h | 2 +- iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp | 26 ++++++++++++++++++- iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.h | 7 +++-- .../ECDb/ECDb/ECSql/ECSqlPreparedStatement.h | 4 +-- .../ECDb/ECDb/ECSql/ECSqlStatementNoopImpls.h | 2 +- iModelCore/ECDb/ECDb/ECSql/IECSqlBinder.cpp | 2 +- iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp | 4 +-- iModelCore/ECDb/ECDb/ECSql/ValueExp.h | 1 + .../ECDb/PublicAPI/ECDb/ECSqlStatement.h | 6 ++--- iModelCore/ECDb/PublicAPI/ECDb/IECSqlBinder.h | 18 ++++++++----- 12 files changed, 54 insertions(+), 26 deletions(-) diff --git a/iModelCore/ECDb/ECDb/ConcurrentQueryManagerImpl.cpp b/iModelCore/ECDb/ECDb/ConcurrentQueryManagerImpl.cpp index 8556ab5ec49..39fa8374271 100644 --- a/iModelCore/ECDb/ECDb/ConcurrentQueryManagerImpl.cpp +++ b/iModelCore/ECDb/ECDb/ConcurrentQueryManagerImpl.cpp @@ -1899,12 +1899,12 @@ bool ECSqlParams::TryBindTo(ECSqlStatement& stmt, std::string& err) const { case ECSqlParam::Type::Id: st = stmt.BindId(index, param.GetValueId()); break; case ECSqlParam::Type::IdSet: { - if(stmt.GetBinderType(index) == BinderInfo::BinderType::VirtualSetECSqlBinderType) + if(stmt.GetBinderInfo(index).CheckIfBinderIsForInVirtualSetOrIdSetVirtualTable() && stmt.GetBinderInfo(index).GetBinderType() == BinderInfo::BinderType::VirtualSetECSqlBinderType) { std::shared_ptr> idSet = std::make_shared>(param.GetValueIdSet()); st = stmt.BindVirtualSet(index, idSet); } - else if(stmt.GetBinderType(index) == BinderInfo::BinderType::ArrayECSqlBinderType) + else if(stmt.GetBinderInfo(index).CheckIfBinderIsForInVirtualSetOrIdSetVirtualTable() && stmt.GetBinderInfo(index).GetBinderType() == BinderInfo::BinderType::ArrayECSqlBinderType) { IECSqlBinder& binder = stmt.GetBinder(index); IdSet set(param.GetValueIdSet()); diff --git a/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp index 4b826d89001..1bb3a17efbf 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp @@ -583,9 +583,9 @@ ECSqlStatus ArrayECSqlBinder::JsonValueBinder::FailIfInvalid() const //--------------------------------------------------------------------------------------- // @bsimethod //--------------------------------------------------------------------------------------- -BinderInfo::BinderType ArrayECSqlBinder::JsonValueBinder::_GetBinderType() +BinderInfo& ArrayECSqlBinder::JsonValueBinder::_GetBinderInfo() { - return m_binderInfo.GetBinderType(); + return m_binderInfo; } diff --git a/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.h b/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.h index cb4bb15c0f3..3e5d9e5af18 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.h +++ b/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.h @@ -65,7 +65,7 @@ struct ArrayECSqlBinder final : public ECSqlBinder IECSqlBinder& _AddArrayElement() override; - BinderInfo::BinderType _GetBinderType() override; + BinderInfo& _GetBinderInfo() override; }; diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp index b87a6e06c55..fcba1f580db 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp @@ -86,7 +86,23 @@ std::unique_ptr ECSqlBinderFactory::CreateBinder(ECSqlPrepareContex { if (FunctionCallExp const* parentExp = exp->GetAsCP()) { if (parentExp->GetFunctionName().EqualsI("InVirtualSet") && parentExp->GetChildren()[0] == ¶meterExp) - return CreateVirtualSetBinder(ctx, parameterExp.GetTypeInfo(), paramNameGen); + { + std::unique_ptr virtualSetBinder = CreateVirtualSetBinder(ctx, parameterExp.GetTypeInfo(), paramNameGen); + virtualSetBinder->GetBinderInfo().SetIfBinderIsForInVirtualSetOrIdSetVirtualTable(true); + return virtualSetBinder; + } + } + } + + if (const Exp* exp = parameterExp.FindParent(Exp::Type::MemberFunctionCall)) + { + if (MemberFunctionCallExp const* parentExp = exp->GetAsCP()) { + if (parentExp->IsTableValuedFunc() && parentExp->GetFunctionName().EqualsI("IdSet") && parentExp->GetChildren()[0] == ¶meterExp) + { + std::unique_ptr arrayECsqlBinder = CreateArrayECSqlBinder(ctx, parameterExp.GetTypeInfo(), paramNameGen); + arrayECsqlBinder->GetBinderInfo().SetIfBinderIsForInVirtualSetOrIdSetVirtualTable(true); + return arrayECsqlBinder; + } } } @@ -179,6 +195,14 @@ std::unique_ptr ECSqlBinderFactory::CreateVirtualSetBinder(ECS return std::unique_ptr(new VirtualSetBinder(ctx, typeInfo, paramNameGen)); } +//--------------------------------------------------------------------------------------- +// @bsimethod +//--------------------------------------------------------------------------------------- +std::unique_ptr ECSqlBinderFactory::CreateArrayECSqlBinder(ECSqlPrepareContext& ctx, ECSqlTypeInfo const& typeInfo, ECSqlBinder::SqlParamNameGenerator& paramNameGen) + { + return std::unique_ptr(new ArrayECSqlBinder(ctx, typeInfo, paramNameGen)); + } + //--------------------------------------------------------------------------------------- // @bsimethod //--------------------------------------------------------------------------------------- diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.h b/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.h index 05144bc71ac..763755bcd16 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.h +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.h @@ -62,7 +62,7 @@ struct ECSqlBinder : IECSqlBinder virtual ECSqlStatus _OnBeforeStep() { return ECSqlStatus::Success; } virtual void _OnClearBindings() {} - BinderInfo::BinderType _GetBinderType() override { return m_binderInfo.GetBinderType(); }; + BinderInfo& _GetBinderInfo() override { return m_binderInfo; }; protected: ECSqlBinder(ECSqlPrepareContext&, ECSqlTypeInfo const&, SqlParamNameGenerator&, int mappedSqlParameterCount, bool hasToCallOnBeforeStep, bool hasToCallOnClearBindings, BinderInfo::BinderType binderType); @@ -89,8 +89,6 @@ struct ECSqlBinder : IECSqlBinder std::vector const& GetMappedSqlParameterNames() const { return m_mappedSqlParameterNames; } - BinderInfo::BinderType GetBinderType() { return _GetBinderType(); } - ECSqlTypeInfo const& GetTypeInfo() const { return m_typeInfo; } ECSqlStatus OnBeforeStep() { return _OnBeforeStep(); } @@ -99,6 +97,7 @@ struct ECSqlBinder : IECSqlBinder struct IdECSqlBinder; struct VirtualSetBinder; +struct ArrayECSqlBinder; //======================================================================================= //! @bsiclass @@ -118,7 +117,7 @@ struct ECSqlBinderFactory final static std::unique_ptr CreateIdBinder(ECSqlPrepareContext&, PropertyMap const&, ECSqlSystemPropertyInfo const&, ECSqlBinder::SqlParamNameGenerator&); static std::unique_ptr CreateIdBinderForQuery(ECSqlPrepareContext&, ECSqlTypeInfo const&, ECSqlBinder::SqlParamNameGenerator&); static std::unique_ptr CreateVirtualSetBinder(ECSqlPrepareContext&, ECSqlTypeInfo const&, ECSqlBinder::SqlParamNameGenerator&); - + static std::unique_ptr CreateArrayECSqlBinder(ECSqlPrepareContext&, ECSqlTypeInfo const&, ECSqlBinder::SqlParamNameGenerator&); }; //======================================================================================= diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h index 6ab3a2402b5..ab681a6bde5 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h @@ -172,7 +172,7 @@ struct CompoundECSqlPreparedStatement : IECSqlPreparedStatement IECSqlBinder& _BindStructMember(ECN::ECPropertyId structMemberPropertyId) override; IECSqlBinder& _AddArrayElement() override; - BinderInfo::BinderType _GetBinderType() override { return m_binderInfo.GetBinderType(); } + BinderInfo& _GetBinderInfo() override { return m_binderInfo; } void _AddBinder(IECSqlBinder& binder) override { BeAssert(m_idBinder == nullptr); m_idBinder = &binder; } @@ -215,7 +215,7 @@ struct CompoundECSqlPreparedStatement : IECSqlPreparedStatement IECSqlBinder& _BindStructMember(ECN::ECPropertyId structMemberPropertyId) override; IECSqlBinder& _AddArrayElement() override; - BinderInfo::BinderType _GetBinderType() override { return m_binderInfo.GetBinderType(); } + BinderInfo& _GetBinderInfo() override { return m_binderInfo; } void _AddBinder(IECSqlBinder& binder) override { m_binders.push_back(&binder); } diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlStatementNoopImpls.h b/iModelCore/ECDb/ECDb/ECSql/ECSqlStatementNoopImpls.h index 922e96d0de7..84ff9197359 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlStatementNoopImpls.h +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlStatementNoopImpls.h @@ -40,7 +40,7 @@ struct NoopECSqlBinder final : public IECSqlBinder IECSqlBinder& _AddArrayElement() override { return *this; } - BinderInfo::BinderType _GetBinderType() override { return m_binderInfo.GetBinderType(); } + BinderInfo& _GetBinderInfo() override { return m_binderInfo; } public: static NoopECSqlBinder& Get(); diff --git a/iModelCore/ECDb/ECDb/ECSql/IECSqlBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/IECSqlBinder.cpp index 65e13b8fcd5..af83fdb220d 100644 --- a/iModelCore/ECDb/ECDb/ECSql/IECSqlBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/IECSqlBinder.cpp @@ -155,6 +155,6 @@ IECSqlBinder& IECSqlBinder::AddArrayElement() { return _AddArrayElement(); } //--------------------------------------------------------------------------------------- // @bsimethod //--------------------------------------------------------------------------------------- -BinderInfo::BinderType IECSqlBinder::GetBinderType() { return _GetBinderType(); } +BinderInfo& IECSqlBinder::GetBinderInfo() { return _GetBinderInfo(); } END_BENTLEY_SQLITE_EC_NAMESPACE \ No newline at end of file diff --git a/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp b/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp index ec7cd96a310..986370a2415 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp @@ -817,7 +817,7 @@ Exp::FinalizeParseStatus MemberFunctionCallExp::_FinalizeParsing(ECSqlParseConte if (mode == Exp::FinalizeParseMode::AfterFinalizingChildren) { if (this->m_tableValuedFunc) { - if(GetFunctionName().EqualsIAscii("IdSet")) + if(m_functionName.EqualsIAscii("IdSet")) { ValueExp const* argExp = GetArgument(0); if(argExp == nullptr) @@ -960,7 +960,7 @@ Utf8String MemberFunctionCallExp::_ToString() const //+---------------+---------------+---------------+---------------+---------------+------ bool MemberFunctionCallExp::_TryDetermineParameterExpType(ECSqlParseContext& ctx, ParameterExp& parameterExp) const { - if(m_functionName.EqualsIAscii("IdSet")) + if(this->m_tableValuedFunc && m_functionName.EqualsIAscii("IdSet")) { parameterExp.SetTargetExpInfo(ECSqlTypeInfo::CreatePrimitive(ECN::PRIMITIVETYPE_Long, true, EXTENDEDTYPENAME_Id)); return true; diff --git a/iModelCore/ECDb/ECDb/ECSql/ValueExp.h b/iModelCore/ECDb/ECDb/ECSql/ValueExp.h index 3ba1fc87b90..02eff702941 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ValueExp.h +++ b/iModelCore/ECDb/ECDb/ECSql/ValueExp.h @@ -365,6 +365,7 @@ struct MemberFunctionCallExp final : ValueExp explicit MemberFunctionCallExp(Utf8StringCR functionName, bool tableValuedFunc) : ValueExp(Type::MemberFunctionCall), m_tableValuedFunc(tableValuedFunc), m_functionName(functionName){} virtual ~MemberFunctionCallExp() {} + bool IsTableValuedFunc() const { return m_tableValuedFunc; } Utf8StringCR GetFunctionName() const { return m_functionName; } BentleyStatus AddArgument(std::unique_ptr, Utf8StringR); ValueExp const* GetArgument(size_t index) const; diff --git a/iModelCore/ECDb/PublicAPI/ECDb/ECSqlStatement.h b/iModelCore/ECDb/PublicAPI/ECDb/ECSqlStatement.h index d38ebce47f3..2fc23d4329d 100644 --- a/iModelCore/ECDb/PublicAPI/ECDb/ECSqlStatement.h +++ b/iModelCore/ECDb/PublicAPI/ECDb/ECSqlStatement.h @@ -249,10 +249,10 @@ struct EXPORT_VTABLE_ATTRIBUTE ECSqlStatement //! @return Parameter binder ECDB_EXPORT IECSqlBinder& GetBinder(int parameterIndex); - //! Gets BinderType for the binder associated with the parameter at the specified index. + //! Gets BinderInfo for the binder associated with the parameter at the specified index. //! @param[in] parameterIndex Parameter index. - //! @return Parameter BinderType - ECDB_EXPORT BinderInfo::BinderType GetBinderType(int parameterIndex) { return GetBinder(parameterIndex).GetBinderType(); } + //! @return Parameter BinderInfo. If there is no valid binder on that index the it will still return BinderInfo with Binder type NoopECSqlBinderType + ECDB_EXPORT BinderInfo const& GetBinderInfo(int parameterIndex) { return GetBinder(parameterIndex).GetBinderInfo(); } //! Gets the parameter index for a named parameter. Will log an error if parameter not found. //! diff --git a/iModelCore/ECDb/PublicAPI/ECDb/IECSqlBinder.h b/iModelCore/ECDb/PublicAPI/ECDb/IECSqlBinder.h index 3c46a644a14..3a9042d1007 100644 --- a/iModelCore/ECDb/PublicAPI/ECDb/IECSqlBinder.h +++ b/iModelCore/ECDb/PublicAPI/ECDb/IECSqlBinder.h @@ -28,14 +28,19 @@ struct BinderInfo final JsonValueBinderType, NoopECSqlBinderType, ProxyECInstanceIdECSqlBinderType, - ProxyECSqlBinderType + ProxyECSqlBinderType, + NotSpecified }; private: - BinderType m_binderType; + BinderType m_binderType = BinderType::NotSpecified; + bool m_binderIsForInVirtualSetOrIdSetVirtualTable = false; public: - BinderInfo(BinderType binderType) : m_binderType(binderType){} + BinderInfo(BinderType binderType) : m_binderType(binderType), m_binderIsForInVirtualSetOrIdSetVirtualTable(false){} + BinderInfo(BinderType binderType, bool binderIsForInVirtualSetOrIdSetVirtualTable) : m_binderType(binderType), m_binderIsForInVirtualSetOrIdSetVirtualTable(binderIsForInVirtualSetOrIdSetVirtualTable){} BinderInfo::BinderType GetBinderType() const { return m_binderType; } + bool CheckIfBinderIsForInVirtualSetOrIdSetVirtualTable() const { return m_binderIsForInVirtualSetOrIdSetVirtualTable; } + void SetIfBinderIsForInVirtualSetOrIdSetVirtualTable(bool val) { m_binderIsForInVirtualSetOrIdSetVirtualTable = val; } }; @@ -85,7 +90,7 @@ struct EXPORT_VTABLE_ATTRIBUTE IECSqlBinder virtual IECSqlBinder& _BindStructMember(ECN::ECPropertyId structMemberPropertyId) = 0; virtual IECSqlBinder& _AddArrayElement() = 0; - virtual BinderInfo::BinderType _GetBinderType() = 0; + virtual BinderInfo& _GetBinderInfo() = 0; protected: @@ -230,9 +235,8 @@ struct EXPORT_VTABLE_ATTRIBUTE IECSqlBinder //! @return The binder for the new array element ECDB_EXPORT IECSqlBinder& AddArrayElement(); - //! Adds a new array element to the array to be bound to the parameter. - //! @return The binder for the new array element - ECDB_EXPORT BinderInfo::BinderType GetBinderType(); + //! @return Gets the BinderInfo for the specific binder + ECDB_EXPORT BinderInfo& GetBinderInfo(); }; END_BENTLEY_SQLITE_EC_NAMESPACE From 66ea5c38d92f653b10d81d06ff6e8b34b7925377 Mon Sep 17 00:00:00 2001 From: SohamBhattacharjee Date: Thu, 3 Oct 2024 20:04:30 +0530 Subject: [PATCH 11/43] Tests Added --- .../NonPublished/ConcurrentQueryTest_V2.cpp | 71 +++- .../NonPublished/ECDbIdSetVirtualTable.cpp | 306 +++--------------- 2 files changed, 95 insertions(+), 282 deletions(-) diff --git a/iModelCore/ECDb/Tests/NonPublished/ConcurrentQueryTest_V2.cpp b/iModelCore/ECDb/Tests/NonPublished/ConcurrentQueryTest_V2.cpp index dd25ea49920..a9273959ea1 100644 --- a/iModelCore/ECDb/Tests/NonPublished/ConcurrentQueryTest_V2.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/ConcurrentQueryTest_V2.cpp @@ -1061,23 +1061,66 @@ TEST_F(ConcurrentQueryFixture, BlobIO) { TEST_F(ConcurrentQueryFixture, ReaderBindingForIdSetVirtualTable) { ASSERT_EQ(DbResult::BE_SQLITE_OK, SetupECDb("ConcurrentQuery_Simple.ecdb")); + { + auto& mgr = ConcurrentQueryMgr::GetInstance(m_ecdb); + BeIdSet idSet; + idSet.insert(BeInt64Id(10)); + idSet.insert(BeInt64Id(20)); + idSet.insert(BeInt64Id(30)); + idSet.insert(BeInt64Id(40)); + int vsRowCount = 0; + + ECSqlReader vsReaderIdSet(mgr, "select id from test.IdSet(?)", + ECSqlParams().BindIdSet(1, idSet)); + + int i = 1; + while(vsReaderIdSet.Next()) { + auto classRow = vsReaderIdSet.GetRow(); + ASSERT_EQ(i*10, classRow[0].asInt64()); + i++; + vsRowCount++; + } + ASSERT_EQ(vsRowCount,4); + } + { + auto& mgr = ConcurrentQueryMgr::GetInstance(m_ecdb); + BeIdSet idSet; + idSet.insert(BeInt64Id(10)); + idSet.insert(BeInt64Id(20)); + idSet.insert(BeInt64Id(30)); + idSet.insert(BeInt64Id(40)); + int vsRowCount = 0; + + ECSqlReader vsReaderIdSet(mgr, "select ECInstanceId from meta.ECClassDef, test.IdSet(?) where ECInstanceId = id", + ECSqlParams().BindIdSet(1, idSet)); + + int i = 1; + while(vsReaderIdSet.Next()) { + auto classRow = vsReaderIdSet.GetRow(); + ASSERT_EQ(i*10, BeStringUtilities::ParseHex(classRow[0].asString().c_str())); + i++; + vsRowCount++; + } + ASSERT_EQ(vsRowCount,4); + } + { + auto& mgr = ConcurrentQueryMgr::GetInstance(m_ecdb); + int vsRowCount = 0; - auto& mgr = ConcurrentQueryMgr::GetInstance(m_ecdb); - BeIdSet idSet; - idSet.insert(BeInt64Id(10)); - idSet.insert(BeInt64Id(20)); - idSet.insert(BeInt64Id(30)); - idSet.insert(BeInt64Id(40)); - int vsRowCount = 0; - - ECSqlReader vsReaderIdSet(mgr, "select id from test.IdSet(?)", - ECSqlParams().BindIdSet(1, idSet)); + ECSqlReader vsReaderIdSet(mgr, "select ECInstanceId from meta.ECClassDef, test.IdSet(?) where ECInstanceId = id", + ECSqlParams().BindId(1, BeInt64Id(33))); - while(vsReaderIdSet.Next()) { - vsRowCount++; + try + { + vsReaderIdSet.Next(); + } + catch (const std::runtime_error& error) + { + ASSERT_EQ(vsRowCount,0) << error.what(); + } + } - ASSERT_EQ(vsRowCount,4); + } - END_ECDBUNITTESTS_NAMESPACE diff --git a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp index 91d6b3c02f1..4ee0687bcb0 100644 --- a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp @@ -13,274 +13,6 @@ BEGIN_ECDBUNITTESTS_NAMESPACE // @bsiclass //+---------------+---------------+---------------+---------------+---------------+------ struct ECDbIdSetVirtualTableTests : ECDbTestFixture {}; -//======================================================================================= -//! Virtual Table to tokenize string -// @bsiclass -//======================================================================================= -// struct IdSetModule : ECDbModule { -// struct IdSetTable : ECDbVirtualTable { -// struct IdSetCursor : ECDbCursor { - -// private: -// Utf8String m_text; -// IdSet* virtualSetPtr = nullptr; -// bset m_idSet; -// bset::iterator m_index; - -// public: -// IdSetCursor(IdSetTable& vt): ECDbCursor(vt){} -// bool Eof() final { return m_index == m_idSet.end() ; } -// DbResult Next() final { -// ++m_index; -// return BE_SQLITE_OK; -// } -// DbResult GetColumn(int i, Context& ctx) final { -// ctx.SetResultInt64((*m_index).GetValue()); -// return BE_SQLITE_OK; -// } -// DbResult GetRowId(int64_t& rowId) final { -// rowId = (*m_index).GetValue(); -// return BE_SQLITE_OK; -// } -//--------------------------------------------------------------------------------------- -// @bsimethod -//--------------------------------------------------------------------------------------- -// DbResult IdSetModule::IdSetTable::IdSetCursor::FilterRapidJSON(BeJsConst& val) { -// if(val.isNull()) -// { -// return BE_SQLITE_ERROR; -// } -// if(val.isNumeric()) -// { -// if(val.asUInt64(-1) == -1) -// return BE_SQLITE_ERROR; -// else -// { -// uint64_t id = val.asUInt64(-1); -// m_idSet.insert(id); -// } -// } -// else if(val.isArray()) -// { -// bool flag = true; -// val.ForEachArrayMember([&](BeJsValue::ArrayIndex, BeJsConst k1) -// { -// if(BE_SQLITE_OK != FilterRapidJSON(k1)) -// flag = true; -// return false; -// }); -// if(flag) -// return BE_SQLITE_ERROR; -// } -// else if(val.isString()) -// { -// if(val.asUInt64(-1) == -1) -// { -// if(val.asString().EqualsIAscii("")) -// return BE_SQLITE_ERROR; -// BeJsDocument doc; -// doc.Parse(val.asString()); -// if(!doc.isArray()) -// return BE_SQLITE_ERROR; -// bool flag = false; -// doc.ForEachArrayMember([&](BeJsValue::ArrayIndex, BeJsConst k1) -// { -// if(BE_SQLITE_OK != FilterRapidJSON(k1)) -// flag = true; -// return false; -// }); -// if(flag) -// return BE_SQLITE_ERROR; -// } -// else -// m_idSet.insert(val.asUInt64(-1)); -// } -// // switch(val.GetType()) -// // { -// // case rapidjson::Type::kArrayType: -// // { -// // for(rapidjson::Value& v: val.GetArray()) -// // { -// // DbResult res = FilterRapidJSON(v); -// // if(res != BE_SQLITE_OK) -// // return res; -// // } -// // break; -// // } -// // case rapidjson::Type::kNumberType: -// // { -// // if(!val.IsUint64()) -// // return BE_SQLITE_ERROR; -// // int64_t idVal = val.GetUint64(); -// // m_idSet.insert(idVal); -// // break; -// // } -// // case rapidjson::Type::kStringType: -// // { -// // BeJsDocument d; -// // d.Parse(val.GetString()); -// // d.Stringify(StringifyFormat::Indented); -// // bool flag = false; -// // if(d.isArray()) -// // { -// // d.ForEachArrayMember([&](BeJsValue::ArrayIndex, BeJsConst k1) -// // { -// // if(d.asUInt64(-1) == -1) -// // flag = true; -// // else -// // m_idSet.insert(k1.asUInt64()); -// // return false; -// // }); -// // if(flag) -// // return BE_SQLITE_ERROR; -// // } -// // else if(d.isString()) -// // { -// // if(d.asUInt64(-1) == -1) -// // return BE_SQLITE_ERROR; -// // else -// // m_idSet.insert(d.asUInt64()); -// // } -// // else -// // return BE_SQLITE_ERROR; -// // break; -// // } -// // case rapidjson::Type::kNullType: -// // { -// // break; -// // } -// // default: -// // // We don't allow any other types here -// // return BE_SQLITE_ERROR; -// // } -// return BE_SQLITE_OK; -// } -// DbResult Filter(int idxNum, const char *idxStr, int argc, DbValue* argv) final { -// int i = 0; -// int flag = false; -// if( idxNum & 1 ){ -// if(argv[i].GetValueType() == DbValueType::TextVal) -// m_text = argv[i++].GetValueText(); -// else -// virtualSetPtr = (IdSet*)argv[i++].GetValuePointer("ID_SET_NAME"); -// flag = true; -// }else{ -// m_text = ""; -// } -// if(flag ) -// { -// if(m_text.size() > 0) -// { -// // Parse String to Js Document and iterate through the array and insert int hex ids as int64 in uniqueIds set. - // BeJsDocument doc; - // doc.Parse(m_text.c_str()); - // doc.ForEachArrayMember([&](BeJsValue::ArrayIndex, BeJsConst k1) - // { - // m_idSet.insert(BeInt64Id(k1.asUInt64())); - // return false; }); - // } -// else if(virtualSetPtr != nullptr) -// { -// IdSet virtualSet = *virtualSetPtr; -// for(auto it = virtualSet.begin();it!=virtualSet.end();it++) -// { -// m_idSet.insert(*it); -// } -// } -// else -// { -// return BE_SQLITE_ERROR; -// } -// } -// std::cout << m_idSet.size() << std::endl; -// m_index = m_idSet.begin(); -// return BE_SQLITE_OK; -// } -// }; -// public: -// IdSetTable(IdSetModule& module): ECDbVirtualTable(module) {} -// DbResult Open(DbCursor*& cur) override { -// cur = new IdSetCursor(*this); -// return BE_SQLITE_OK; -// } -// DbResult BestIndex(IndexInfo& indexInfo) final { -// int i, j; /* Loop over constraints */ -// int idxNum = 0; /* The query plan bitmask */ -// int unusableMask = 0; /* Mask of unusable constraints */ -// int nArg = 0; /* Number of arguments that seriesFilter() expects */ -// int aIdx[2]; /* Constraints on start, stop, and step */ -// const int SQLITE_SERIES_CONSTRAINT_VERIFY = 0; -// aIdx[0] = aIdx[1] = -1; -// int nConstraint = indexInfo.GetConstraintCount(); - -// for(i=0; iGetColumn()< 0) continue; -// iCol = pConstraint->GetColumn(); -// iMask = 1 << iCol; -// if (!pConstraint->IsUsable()){ -// unusableMask |= iMask; -// continue; -// } else if (pConstraint->GetOp() == IndexInfo::Operator::EQ ){ -// idxNum |= iMask; -// aIdx[iCol] = i; -// } -// } -// for( i = 0; i < 2; i++) { -// if( (j = aIdx[i]) >= 0 ) { -// indexInfo.GetConstraintUsage(j)->SetArgvIndex(++nArg); -// indexInfo.GetConstraintUsage(j)->SetOmit(!SQLITE_SERIES_CONSTRAINT_VERIFY); -// } -// } - -// if ((unusableMask & ~idxNum)!=0 ){ -// return BE_SQLITE_CONSTRAINT; -// } - -// indexInfo.SetEstimatedCost(2.0); -// indexInfo.SetEstimatedRows(1000); -// if( indexInfo.GetIndexOrderByCount() >= 1 && indexInfo.GetOrderBy(0)->GetColumn() == 0 ) { -// if( indexInfo.GetOrderBy(0) ->GetDesc()){ -// idxNum |= 8; -// } else { -// idxNum |= 16; -// } -// indexInfo.SetOrderByConsumed(true); -// } -// indexInfo.SetIdxNum(idxNum); -// return BE_SQLITE_OK; -// } -// }; -// public: -// IdSetModule(ECDbR db): ECDbModule( -// db, -// "IdSet", -// "CREATE TABLE x(id hidden)", -// R"xml( -// -// -// -// -// -// -// -// -// -// -// -// )xml") {} -// DbResult Connect(DbVirtualTable*& out, Config& conf, int argc, const char* const* argv) final { -// out = new IdSetTable(*this); -// conf.SetTag(Config::Tags::Innocuous); -// return BE_SQLITE_OK; -// } -// }; //--------------------------------------------------------------------------------------- // @bsimethod @@ -603,6 +335,44 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT ECClassId FROM test.IdSet('[1,2,3,4,5]'), meta.ECClassDef where ECClassId = id group by ECClassId"))); ASSERT_STREQ("7 0 0 SCAN IdSet VIRTUAL TABLE INDEX 1: (null) (null) (null) (null);15 0 0 SCAN main.ec_Class USING COVERING INDEX ix_ec_Class_Name (null) (null) (null) (null);17 0 0 USE TEMP B-TREE FOR GROUP BY (null) (null) (null) (null)", m_ecdb.ExplainQuery(stmt.GetNativeSql(), true).c_str()); } + { + std::vector hexIds = std::vector{"0x1", "0x2", "0x3", "4", "5"}; + + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT ECInstanceId FROM test.IdSet(?), meta.ECClassDef where ECInstanceId = id group by ECInstanceId"))); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + for(int i =0;i Date: Thu, 3 Oct 2024 20:20:59 +0530 Subject: [PATCH 12/43] Crash for following expression stopped by providing null checks SELECT x FROM (with cte(x) as(select ECInstanceId from meta.ECClassDef) select x from cte), test.IdSet('[1,2,3,4,5]') where id = x group by x --- iModelCore/ECDb/ECDb/ECSql/ClassRefExp.cpp | 4 ++++ iModelCore/ECDb/ECDb/ECSql/ClassRefExp.h | 2 +- iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/iModelCore/ECDb/ECDb/ECSql/ClassRefExp.cpp b/iModelCore/ECDb/ECDb/ECSql/ClassRefExp.cpp index f74e801a44e..fc995b7eb84 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ClassRefExp.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ClassRefExp.cpp @@ -45,6 +45,8 @@ Utf8StringCR TableValuedFunctionExp::_GetId() const { +---------------+---------------+---------------+---------------+---------------+------*/ void TableValuedFunctionExp::_ExpandSelectAsterisk( std::vector>& expandedSelectClauseItemList, ECSqlParseContext const&) const { + if(m_virtualEntityClass == nullptr) + return; BeAssert(m_virtualEntityClass != nullptr); auto alias = GetAlias(); for(auto& prop : m_virtualEntityClass->GetProperties()) { @@ -61,6 +63,8 @@ void TableValuedFunctionExp::_ExpandSelectAsterisk( * @bsimethod +---------------+---------------+---------------+---------------+---------------+------*/ PropertyMatchResult TableValuedFunctionExp::_FindProperty(ECSqlParseContext& ctx, PropertyPath const& propertyPath, const PropertyMatchOptions& options) const { + if(m_virtualEntityClass == nullptr) + return PropertyMatchResult::NotFound(); BeAssert(m_virtualEntityClass != nullptr); if (propertyPath.Size() == 1) { auto property = m_virtualEntityClass->GetPropertyP(propertyPath.First().GetName()); diff --git a/iModelCore/ECDb/ECDb/ECSql/ClassRefExp.h b/iModelCore/ECDb/ECDb/ECSql/ClassRefExp.h index 74190bb52cd..fe3ff00fbdf 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ClassRefExp.h +++ b/iModelCore/ECDb/ECDb/ECSql/ClassRefExp.h @@ -241,7 +241,7 @@ struct TableValuedFunctionExp : RangeClassRefExp { friend struct ECSqlParser; private: - ECN::ECEntityClassCP m_virtualEntityClass; + ECN::ECEntityClassCP m_virtualEntityClass = nullptr; Utf8String m_schemaName; virtual void _OnAliasChanged() override {} virtual FinalizeParseStatus _FinalizeParsing(ECSqlParseContext&, FinalizeParseMode) override; diff --git a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp index 4ee0687bcb0..8a61cafb1a9 100644 --- a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp @@ -317,7 +317,7 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { } { ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT x FROM test.IdSet('[1,2,3,4,5]'), (with cte(x) as(select ECInstanceId from meta.ECClassDef) select x from cte) where id = x group by x"))); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT x FROM (with cte(x) as(select ECInstanceId from meta.ECClassDef) select x from cte), test.IdSet('[1,2,3,4,5]') where id = x group by x"))); int i = 1; while (stmt.Step() == BE_SQLITE_ROW) { From c9da214eb8cfb8ff6eeb0de5c3ababe90abf3480 Mon Sep 17 00:00:00 2001 From: SohamBhattacharjee Date: Fri, 4 Oct 2024 11:50:09 +0530 Subject: [PATCH 13/43] Kepping concurrentQueryImpl as close to as it was with minimal changes --- .../ECDb/ECDb/ConcurrentQueryManagerImpl.cpp | 18 +++++++++++------- iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp | 8 ++------ iModelCore/ECDb/PublicAPI/ECDb/IECSqlBinder.h | 10 +++++----- .../NonPublished/ConcurrentQueryTest_V2.cpp | 12 ++++-------- 4 files changed, 22 insertions(+), 26 deletions(-) diff --git a/iModelCore/ECDb/ECDb/ConcurrentQueryManagerImpl.cpp b/iModelCore/ECDb/ECDb/ConcurrentQueryManagerImpl.cpp index 39fa8374271..3bd64b95b64 100644 --- a/iModelCore/ECDb/ECDb/ConcurrentQueryManagerImpl.cpp +++ b/iModelCore/ECDb/ECDb/ConcurrentQueryManagerImpl.cpp @@ -1899,29 +1899,33 @@ bool ECSqlParams::TryBindTo(ECSqlStatement& stmt, std::string& err) const { case ECSqlParam::Type::Id: st = stmt.BindId(index, param.GetValueId()); break; case ECSqlParam::Type::IdSet: { - if(stmt.GetBinderInfo(index).CheckIfBinderIsForInVirtualSetOrIdSetVirtualTable() && stmt.GetBinderInfo(index).GetBinderType() == BinderInfo::BinderType::VirtualSetECSqlBinderType) + BinderInfo const& binderInfo = stmt.GetBinderInfo(index); + if(binderInfo.GetBinderType() == BinderInfo::BinderType::VirtualSetECSqlBinderType) { std::shared_ptr> idSet = std::make_shared>(param.GetValueIdSet()); st = stmt.BindVirtualSet(index, idSet); } - else if(stmt.GetBinderInfo(index).CheckIfBinderIsForInVirtualSetOrIdSetVirtualTable() && stmt.GetBinderInfo(index).GetBinderType() == BinderInfo::BinderType::ArrayECSqlBinderType) + else if(binderInfo.GetBinderType() == BinderInfo::BinderType::ArrayECSqlBinderType && binderInfo.CheckIfBinderIsForIdSetVirtualTable()) { + bool allElementsAdded = true; IECSqlBinder& binder = stmt.GetBinder(index); IdSet set(param.GetValueIdSet()); for(auto& ids: set) { st = ids.IsValid() ? binder.AddArrayElement().BindInt64((int64_t) ids.GetValue()) : binder.AddArrayElement().BindNull(); - if(st != ECSqlStatus::Success) + if(!st.IsSuccess()) { - err = SqlPrintfString("Failed to bind id '%s' while binding IdSet in IdSet VT", ids.ToHexStr().c_str()).GetUtf8CP(); - return false; + allElementsAdded = false; } } + if(allElementsAdded) // If even one array element has failed to be added we set the status for the entire operation as ECSqlStatus::Error although for the time being we don't do anything with status even if it fails + st = ECSqlStatus::Success; + else + st = ECSqlStatus::Error; } else { - err = "Unsuported binder type for IdSet"; - return false; + st = ECSqlStatus::Error; } break; diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp index fcba1f580db..b9151f7052e 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp @@ -86,11 +86,7 @@ std::unique_ptr ECSqlBinderFactory::CreateBinder(ECSqlPrepareContex { if (FunctionCallExp const* parentExp = exp->GetAsCP()) { if (parentExp->GetFunctionName().EqualsI("InVirtualSet") && parentExp->GetChildren()[0] == ¶meterExp) - { - std::unique_ptr virtualSetBinder = CreateVirtualSetBinder(ctx, parameterExp.GetTypeInfo(), paramNameGen); - virtualSetBinder->GetBinderInfo().SetIfBinderIsForInVirtualSetOrIdSetVirtualTable(true); - return virtualSetBinder; - } + return CreateVirtualSetBinder(ctx, parameterExp.GetTypeInfo(), paramNameGen); } } @@ -100,7 +96,7 @@ std::unique_ptr ECSqlBinderFactory::CreateBinder(ECSqlPrepareContex if (parentExp->IsTableValuedFunc() && parentExp->GetFunctionName().EqualsI("IdSet") && parentExp->GetChildren()[0] == ¶meterExp) { std::unique_ptr arrayECsqlBinder = CreateArrayECSqlBinder(ctx, parameterExp.GetTypeInfo(), paramNameGen); - arrayECsqlBinder->GetBinderInfo().SetIfBinderIsForInVirtualSetOrIdSetVirtualTable(true); + arrayECsqlBinder->GetBinderInfo().SetIfBinderIsForIdSetVirtualTable(true); return arrayECsqlBinder; } } diff --git a/iModelCore/ECDb/PublicAPI/ECDb/IECSqlBinder.h b/iModelCore/ECDb/PublicAPI/ECDb/IECSqlBinder.h index 3a9042d1007..e2eb02ec2a3 100644 --- a/iModelCore/ECDb/PublicAPI/ECDb/IECSqlBinder.h +++ b/iModelCore/ECDb/PublicAPI/ECDb/IECSqlBinder.h @@ -34,13 +34,13 @@ struct BinderInfo final private: BinderType m_binderType = BinderType::NotSpecified; - bool m_binderIsForInVirtualSetOrIdSetVirtualTable = false; + bool m_binderIsForIdSetVirtualTable = false; public: - BinderInfo(BinderType binderType) : m_binderType(binderType), m_binderIsForInVirtualSetOrIdSetVirtualTable(false){} - BinderInfo(BinderType binderType, bool binderIsForInVirtualSetOrIdSetVirtualTable) : m_binderType(binderType), m_binderIsForInVirtualSetOrIdSetVirtualTable(binderIsForInVirtualSetOrIdSetVirtualTable){} + BinderInfo(BinderType binderType) : m_binderType(binderType), m_binderIsForIdSetVirtualTable(false){} + BinderInfo(BinderType binderType, bool binderIsForInVirtualSetOrIdSetVirtualTable) : m_binderType(binderType), m_binderIsForIdSetVirtualTable(binderIsForInVirtualSetOrIdSetVirtualTable){} BinderInfo::BinderType GetBinderType() const { return m_binderType; } - bool CheckIfBinderIsForInVirtualSetOrIdSetVirtualTable() const { return m_binderIsForInVirtualSetOrIdSetVirtualTable; } - void SetIfBinderIsForInVirtualSetOrIdSetVirtualTable(bool val) { m_binderIsForInVirtualSetOrIdSetVirtualTable = val; } + bool CheckIfBinderIsForIdSetVirtualTable() const { return m_binderIsForIdSetVirtualTable; } + void SetIfBinderIsForIdSetVirtualTable(bool val) { m_binderIsForIdSetVirtualTable = val; } }; diff --git a/iModelCore/ECDb/Tests/NonPublished/ConcurrentQueryTest_V2.cpp b/iModelCore/ECDb/Tests/NonPublished/ConcurrentQueryTest_V2.cpp index a9273959ea1..9b393a9f4f1 100644 --- a/iModelCore/ECDb/Tests/NonPublished/ConcurrentQueryTest_V2.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/ConcurrentQueryTest_V2.cpp @@ -1076,7 +1076,7 @@ TEST_F(ConcurrentQueryFixture, ReaderBindingForIdSetVirtualTable) { int i = 1; while(vsReaderIdSet.Next()) { auto classRow = vsReaderIdSet.GetRow(); - ASSERT_EQ(i*10, classRow[0].asInt64()); + ASSERT_EQ(i*10, BeStringUtilities::ParseHex(classRow[0].asString().c_str())); i++; vsRowCount++; } @@ -1110,15 +1110,11 @@ TEST_F(ConcurrentQueryFixture, ReaderBindingForIdSetVirtualTable) { ECSqlReader vsReaderIdSet(mgr, "select ECInstanceId from meta.ECClassDef, test.IdSet(?) where ECInstanceId = id", ECSqlParams().BindId(1, BeInt64Id(33))); - try - { - vsReaderIdSet.Next(); - } - catch (const std::runtime_error& error) + while(vsReaderIdSet.Next()) { - ASSERT_EQ(vsRowCount,0) << error.what(); + vsRowCount++; } - + ASSERT_EQ(vsRowCount,0); } } From cfc8683933a51815ac5b5026813d4e9ab325955e Mon Sep 17 00:00:00 2001 From: SohamBhattacharjee Date: Fri, 4 Oct 2024 12:50:03 +0530 Subject: [PATCH 14/43] schema name changed to ECVLib and also file name changed --- iModelCore/ECDb/ECDb/BuiltInVTabs.cpp | 7 +-- iModelCore/ECDb/ECDb/BuiltInVTabs.h | 6 +- .../NonPublished/ConcurrentQueryTest_V2.cpp | 6 +- ...ble.cpp => ECDbIdSetVirtualTableTests.cpp} | 56 +++++++++---------- .../Performance/PerformanceIdSetTests.cpp | 8 +-- 5 files changed, 38 insertions(+), 45 deletions(-) rename iModelCore/ECDb/Tests/NonPublished/{ECDbIdSetVirtualTable.cpp => ECDbIdSetVirtualTableTests.cpp} (87%) diff --git a/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp b/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp index 2f6c28e9463..0e154e055c7 100644 --- a/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp +++ b/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp @@ -3,7 +3,6 @@ * See LICENSE.md in the repository root for full copyright notice. *--------------------------------------------------------------------------------------------*/ #include "ECDbPch.h" -#include "iostream" USING_NAMESPACE_BENTLEY_EC BEGIN_BENTLEY_SQLITE_EC_NAMESPACE @@ -343,10 +342,8 @@ DbResult IdSetModule::Connect(DbVirtualTable*& out, Config& conf, int argc, cons DbResult RegisterBuildInVTabs(ECDbR ecdb) { DbResult rc = (new ClassPropsModule(ecdb))->Register(); - if(rc != BE_SQLITE_OK) - return rc; - rc = (new IdSetModule(ecdb))->Register(); - if(rc != BE_SQLITE_OK) + DbResult rcIdSet = (new IdSetModule(ecdb))->Register(); + if(rc != BE_SQLITE_OK || rcIdSet != BE_SQLITE_OK) return rc; return BE_SQLITE_OK; } diff --git a/iModelCore/ECDb/ECDb/BuiltInVTabs.h b/iModelCore/ECDb/ECDb/BuiltInVTabs.h index 78ce9e03b5b..9e0687d09b7 100644 --- a/iModelCore/ECDb/ECDb/BuiltInVTabs.h +++ b/iModelCore/ECDb/ECDb/BuiltInVTabs.h @@ -84,8 +84,8 @@ struct IdSetModule : ECDbModule { "CREATE TABLE x(id, json_array_ids hidden)", R"xml( @@ -96,7 +96,7 @@ struct IdSetModule : ECDbModule { - + )xml" ) {} diff --git a/iModelCore/ECDb/Tests/NonPublished/ConcurrentQueryTest_V2.cpp b/iModelCore/ECDb/Tests/NonPublished/ConcurrentQueryTest_V2.cpp index 9b393a9f4f1..4fcaba7a3f8 100644 --- a/iModelCore/ECDb/Tests/NonPublished/ConcurrentQueryTest_V2.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/ConcurrentQueryTest_V2.cpp @@ -1070,7 +1070,7 @@ TEST_F(ConcurrentQueryFixture, ReaderBindingForIdSetVirtualTable) { idSet.insert(BeInt64Id(40)); int vsRowCount = 0; - ECSqlReader vsReaderIdSet(mgr, "select id from test.IdSet(?)", + ECSqlReader vsReaderIdSet(mgr, "select id from ECVLib.IdSet(?)", ECSqlParams().BindIdSet(1, idSet)); int i = 1; @@ -1091,7 +1091,7 @@ TEST_F(ConcurrentQueryFixture, ReaderBindingForIdSetVirtualTable) { idSet.insert(BeInt64Id(40)); int vsRowCount = 0; - ECSqlReader vsReaderIdSet(mgr, "select ECInstanceId from meta.ECClassDef, test.IdSet(?) where ECInstanceId = id", + ECSqlReader vsReaderIdSet(mgr, "select ECInstanceId from meta.ECClassDef, ECVLib.IdSet(?) where ECInstanceId = id", ECSqlParams().BindIdSet(1, idSet)); int i = 1; @@ -1107,7 +1107,7 @@ TEST_F(ConcurrentQueryFixture, ReaderBindingForIdSetVirtualTable) { auto& mgr = ConcurrentQueryMgr::GetInstance(m_ecdb); int vsRowCount = 0; - ECSqlReader vsReaderIdSet(mgr, "select ECInstanceId from meta.ECClassDef, test.IdSet(?) where ECInstanceId = id", + ECSqlReader vsReaderIdSet(mgr, "select ECInstanceId from meta.ECClassDef, ECVLib.IdSet(?) where ECInstanceId = id", ECSqlParams().BindId(1, BeInt64Id(33))); while(vsReaderIdSet.Next()) diff --git a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp similarity index 87% rename from iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp rename to iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp index 8a61cafb1a9..36805859030 100644 --- a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTable.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp @@ -21,7 +21,7 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { ASSERT_EQ(BE_SQLITE_OK, SetupECDb("vtab.ecdb")); { ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet('[1,2,3,4,5]')"))); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM ECVLib.IdSet('[1,2,3,4,5]')"))); int i = 0; while (stmt.Step() == BE_SQLITE_ROW) @@ -32,7 +32,7 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { } { ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet('[1,2,3,4,5]') where id = 1"))); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM ECVLib.IdSet('[1,2,3,4,5]') where id = 1"))); int i = 0; while (stmt.Step() == BE_SQLITE_ROW) @@ -43,7 +43,7 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { } { ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet('[1,2,3,4,5]') where id > 1"))); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM ECVLib.IdSet('[1,2,3,4,5]') where id > 1"))); int i = 1; while (stmt.Step() == BE_SQLITE_ROW) @@ -56,7 +56,7 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { std::vector hexIds = std::vector{"0x1", "0x2", "0x3", "4", "5"}; ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM test.IdSet(?)"))); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM ECVLib.IdSet(?)"))); IECSqlBinder& arrayBinder = stmt.GetBinder(1); for(int i =0;i hexIds = std::vector{"0x1", "0x2", "0x3", "4", "5"}; ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT ECInstanceId FROM test.IdSet(?), meta.ECClassDef where ECInstanceId = id group by ECInstanceId"))); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT ECInstanceId FROM ECVLib.IdSet(?), meta.ECClassDef where ECInstanceId = id group by ECInstanceId"))); IECSqlBinder& arrayBinder = stmt.GetBinder(1); for(int i =0;i emptyIdset; ASSERT_EQ(ECSqlStatus::Success, stmt_With_InvirtualSet.Prepare(m_ecdb, "with cnt(x) as (values(1) union select x+1 from cnt where x < 1000000 ) select * from cnt where invirtualset(?, x)")); - ASSERT_EQ(ECSqlStatus::Success, stmt_With_IdSet.Prepare(m_ecdb, "select x from test.IdSet(?), (with cnt(x) as (values(1) union select x+1 from cnt where x < 1000000 ) select * from cnt) where id = x")); + ASSERT_EQ(ECSqlStatus::Success, stmt_With_IdSet.Prepare(m_ecdb, "select x from ECVLib.IdSet(?), (with cnt(x) as (values(1) union select x+1 from cnt where x < 1000000 ) select * from cnt) where id = x")); IECSqlBinder& binder = stmt_With_IdSet.GetBinder(1); for(int i = 0;i<2000;i++) @@ -108,7 +106,5 @@ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet_with } timer_IdSet.Stop(); LOGTODB(TEST_DETAILS, timer_IdSet.GetElapsed(), i); - - ASSERT_EQ(true, timer_IdSet.GetElapsedSeconds() Date: Fri, 4 Oct 2024 14:06:25 +0530 Subject: [PATCH 15/43] cleanup --- iModelCore/ECDb/ECDb/BuiltInFuncs.cpp | 4 +--- iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp | 2 +- iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp | 2 +- iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h | 1 - 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/iModelCore/ECDb/ECDb/BuiltInFuncs.cpp b/iModelCore/ECDb/ECDb/BuiltInFuncs.cpp index d3936654efb..3ec059148a4 100644 --- a/iModelCore/ECDb/ECDb/BuiltInFuncs.cpp +++ b/iModelCore/ECDb/ECDb/BuiltInFuncs.cpp @@ -414,9 +414,7 @@ void InstanceOfFunc::_ComputeScalar(Context& ctx, int nArgs, DbValue* args) ctx.SetResultError("ec_instanceof() db is close"); return; } - stmt-> - - BindVirtualSet(1, classIds); + stmt->BindVirtualSet(1, classIds); stmt->BindId(2, curId); ctx.SetResultInt(stmt->Step() == BE_SQLITE_ROW ? 1 : 0); } diff --git a/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp index 1bb3a17efbf..c180e641051 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp @@ -67,7 +67,7 @@ ECSqlStatus ArrayECSqlBinder::_OnBeforeStep() // @bsimethod //--------------------------------------------------------------------------------------- ArrayECSqlBinder::JsonValueBinder::JsonValueBinder(ECDbCR ecdb, ECSqlTypeInfo const& typeInfo, rapidjson::Value& json, rapidjson::MemoryPoolAllocator<>& jsonAllocator) - : m_ecdb(&ecdb), m_typeInfo(typeInfo), m_json(&json), m_jsonAllocator(&jsonAllocator), m_currentArrayElementBinder(nullptr), m_binderInfo(BinderInfo::BinderType::JsonValueBinderType) + : IECSqlBinder(), m_ecdb(&ecdb), m_typeInfo(typeInfo), m_json(&json), m_jsonAllocator(&jsonAllocator), m_currentArrayElementBinder(nullptr), m_binderInfo(BinderInfo::BinderType::JsonValueBinderType) { BeAssert(m_json != nullptr); BeAssert(m_jsonAllocator != nullptr); diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp index d94020c7433..3eb8b031ed3 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp @@ -269,7 +269,7 @@ ECSqlStatus SingleECSqlPreparedStatement::_Reset() if (nativeSqlStat != BE_SQLITE_OK) return ECSqlStatus(nativeSqlStat); - SetOnBeforeFirstStepNotCalled(true); + m_onBeforeFirstStepNotCalled = true; return ECSqlStatus::Success; } diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h index ab681a6bde5..6729a5eb6aa 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h @@ -104,7 +104,6 @@ struct SingleECSqlPreparedStatement : IECSqlPreparedStatement ECSqlParameterMap& GetParameterMapR() { return m_parameterMap; } BeSQLite::Statement& GetSqliteStatement() { return m_sqliteStatement; } bool GetOnBeforeFirstStepNotCalled() { return m_onBeforeFirstStepNotCalled; } - void SetOnBeforeFirstStepNotCalled(bool val) { m_onBeforeFirstStepNotCalled = val; } }; //======================================================================================= From b73ccf66f86a8f9b02f8c3a613c0c7075891d3a8 Mon Sep 17 00:00:00 2001 From: SohamBhattacharjee Date: Fri, 4 Oct 2024 14:30:02 +0530 Subject: [PATCH 16/43] more cleanup --- iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp index 3eb8b031ed3..9dcf2619368 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp @@ -215,7 +215,7 @@ DbResult SingleECSqlPreparedStatement::DoStep() { if (!m_parameterMap.OnBeforeStep().IsSuccess()) return BE_SQLITE_ERROR; - SetOnBeforeFirstStepNotCalled(false); + m_onBeforeFirstStepNotCalled = false; } From f484fe919acedc4478f8f863cf8e4102890d643c Mon Sep 17 00:00:00 2001 From: SohamBhattacharjee Date: Fri, 4 Oct 2024 16:45:06 +0530 Subject: [PATCH 17/43] Performanvce Tests Updated --- .../Performance/PerformanceIdSetTests.cpp | 90 ++++++++++++++++--- 1 file changed, 80 insertions(+), 10 deletions(-) diff --git a/iModelCore/ECDb/Tests/Performance/PerformanceIdSetTests.cpp b/iModelCore/ECDb/Tests/Performance/PerformanceIdSetTests.cpp index b517c153289..a6ac618f586 100644 --- a/iModelCore/ECDb/Tests/Performance/PerformanceIdSetTests.cpp +++ b/iModelCore/ECDb/Tests/Performance/PerformanceIdSetTests.cpp @@ -44,19 +44,19 @@ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet) i++; } timer_InVirtual_Set.Stop(); - LOGTODB(TEST_DETAILS, timer_InVirtual_Set.GetElapsedSeconds(), i); + LOGTODB(TEST_DETAILS, timer_InVirtual_Set.GetElapsedSeconds(), i, SqlPrintfString("Statement: \"%s\"", stmt_With_InvirtualSet.GetECSql())); iterator_set = emptyIdset.begin(); i = 0; StopWatch timer_IdSet(true); - while(stmt_With_InvirtualSet.Step() == BE_SQLITE_ROW) + while(stmt_With_IdSet.Step() == BE_SQLITE_ROW) { - ASSERT_EQ((*iterator_set).GetValue(), stmt_With_InvirtualSet.GetValueInt64(0)); + ASSERT_EQ((*iterator_set).GetValue(), stmt_With_IdSet.GetValueInt64(0)); ++iterator_set; i++; } timer_IdSet.Stop(); - LOGTODB(TEST_DETAILS, timer_IdSet.GetElapsed(), i); + LOGTODB(TEST_DETAILS, timer_IdSet.GetElapsedSeconds(), i, SqlPrintfString("Statement: \"%s\"", stmt_With_IdSet.GetECSql())); } //--------------------------------------------------------------------------------------- @@ -70,12 +70,12 @@ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet_with IdSet emptyIdset; ASSERT_EQ(ECSqlStatus::Success, stmt_With_InvirtualSet.Prepare(m_ecdb, "with cnt(x) as (values(1) union select x+1 from cnt where x < 1000000 ) select * from cnt where invirtualset(?, x)")); - ASSERT_EQ(ECSqlStatus::Success, stmt_With_IdSet.Prepare(m_ecdb, "select x from ECVLib.IdSet(?), (with cnt(x) as (values(1) union select x+1 from cnt where x < 1000000 ) select * from cnt) where id = x")); + ASSERT_EQ(ECSqlStatus::Success, stmt_With_IdSet.Prepare(m_ecdb, "select x from ECVLib.IdSet(?), (with cnt(x) as (values(1) union select x+1 from cnt where x < 1000000 ) select * from cnt) where x = id")); IECSqlBinder& binder = stmt_With_IdSet.GetBinder(1); for(int i = 0;i<2000;i++) { - int randNum = std::rand()%(50000-1 + 1) + 1; + int randNum = i+1; ASSERT_EQ(ECSqlStatus::Success, binder.AddArrayElement().BindInt64(randNum)); emptyIdset.insert(BeInt64Id(randNum)); } @@ -93,18 +93,88 @@ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet_with i++; } timer_InVirtual_Set.Stop(); - LOGTODB(TEST_DETAILS, timer_InVirtual_Set.GetElapsedSeconds(), i); + LOGTODB(TEST_DETAILS, timer_InVirtual_Set.GetElapsedSeconds(), i, SqlPrintfString("Statement: \"%s\"", stmt_With_InvirtualSet.GetECSql())); iterator_set = emptyIdset.begin(); i = 0; StopWatch timer_IdSet(true); - while(stmt_With_InvirtualSet.Step() == BE_SQLITE_ROW) + while(stmt_With_IdSet.Step() == BE_SQLITE_ROW) { - ASSERT_EQ((*iterator_set).GetValue(), stmt_With_InvirtualSet.GetValueInt64(0)); + ASSERT_EQ((*iterator_set).GetValue(), stmt_With_IdSet.GetValueInt64(0)); ++iterator_set; i++; } timer_IdSet.Stop(); - LOGTODB(TEST_DETAILS, timer_IdSet.GetElapsed(), i); + LOGTODB(TEST_DETAILS, timer_IdSet.GetElapsedSeconds(), i, SqlPrintfString("Statement: \"%s\"", stmt_With_IdSet.GetECSql())); + } + + //--------------------------------------------------------------------------------------- +// @bsiclass +//+---------------+---------------+---------------+---------------+---------------+------ +TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet_with_custom_schema) + { + ASSERT_EQ(BentleyStatus::SUCCESS, SetupECDb("SimpleComparisonBetweenInVirtualSet_and_IdSet_with_custom_schema.ecdb", SchemaItem(R"xml( + + + + + )xml"))); + + ECSqlStatement insert_stmt; + ASSERT_EQ(ECSqlStatus::Success, insert_stmt.Prepare(m_ecdb, "insert into ts.Foo(Ids) values(?)")); + for(int i = 0;i<1000000;i++) + { + insert_stmt.ClearBindings(); + insert_stmt.Reset(); + int rand = (i+1) % 70000; + insert_stmt.BindInt64(1, rand); + ASSERT_EQ(BE_SQLITE_DONE, insert_stmt.Step()); + } + + std::vector v = {"ECInstanceId", "Ids"}; + for(auto& str: v) + { + ECSqlStatement stmt_With_InvirtualSet; + ECSqlStatement stmt_With_IdSet; + IdSet emptyIdset; + + ASSERT_EQ(ECSqlStatus::Success, stmt_With_InvirtualSet.Prepare(m_ecdb,SqlPrintfString("select %s from ts.Foo where invirtualset(?, %s) group by %s", str.c_str(), str.c_str(), str.c_str()))); + ASSERT_EQ(ECSqlStatus::Success, stmt_With_IdSet.Prepare(m_ecdb,SqlPrintfString("select %s from ECVLib.IdSet(?), ts.Foo where %s = id group by %s", str.c_str(), str.c_str(), str.c_str()))); + + IECSqlBinder& binder = stmt_With_IdSet.GetBinder(1); + for(int i = 0;i<2000;i++) + { + int randNum = i+1; + ASSERT_EQ(ECSqlStatus::Success, binder.AddArrayElement().BindInt64(randNum)); + emptyIdset.insert(BeInt64Id(randNum)); + } + std::shared_ptr> idSetPtr = std::make_shared>(emptyIdset); + ASSERT_EQ(ECSqlStatus::Success, stmt_With_InvirtualSet.BindVirtualSet(1, idSetPtr)); + + + auto iterator_set = emptyIdset.begin(); + int i = 0; + StopWatch timer_InVirtual_Set(true); + while(stmt_With_InvirtualSet.Step() == BE_SQLITE_ROW) + { + ASSERT_EQ((*iterator_set).GetValue(), stmt_With_InvirtualSet.GetValueInt64(0)); + ++iterator_set; + i++; + } + timer_InVirtual_Set.Stop(); + LOGTODB(TEST_DETAILS, timer_InVirtual_Set.GetElapsedSeconds(), i, SqlPrintfString("Statement: \"%s\"", stmt_With_InvirtualSet.GetECSql())); + + iterator_set = emptyIdset.begin(); + i = 0; + StopWatch timer_IdSet(true); + while(stmt_With_IdSet.Step() == BE_SQLITE_ROW) + { + ASSERT_EQ((*iterator_set).GetValue(), stmt_With_IdSet.GetValueInt64(0)); + ++iterator_set; + i++; + } + timer_IdSet.Stop(); + LOGTODB(TEST_DETAILS, timer_IdSet.GetElapsedSeconds(), i, SqlPrintfString("Statement: \"%s\"", stmt_With_IdSet.GetECSql())); + } } END_ECDBUNITTESTS_NAMESPACE \ No newline at end of file From 855013c10af8402718d2402068940df1a167c56f Mon Sep 17 00:00:00 2001 From: SohamBhattacharjee Date: Fri, 4 Oct 2024 18:06:48 +0530 Subject: [PATCH 18/43] some comments resolved --- iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp b/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp index 986370a2415..0ba55c84c4e 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp @@ -816,7 +816,7 @@ Exp::FinalizeParseStatus MemberFunctionCallExp::_FinalizeParsing(ECSqlParseConte { if (mode == Exp::FinalizeParseMode::AfterFinalizingChildren) { - if (this->m_tableValuedFunc) { + if (this->IsTableValuedFunc()) { if(m_functionName.EqualsIAscii("IdSet")) { ValueExp const* argExp = GetArgument(0); @@ -960,7 +960,7 @@ Utf8String MemberFunctionCallExp::_ToString() const //+---------------+---------------+---------------+---------------+---------------+------ bool MemberFunctionCallExp::_TryDetermineParameterExpType(ECSqlParseContext& ctx, ParameterExp& parameterExp) const { - if(this->m_tableValuedFunc && m_functionName.EqualsIAscii("IdSet")) + if(this->IsTableValuedFunc() && m_functionName.EqualsIAscii("IdSet")) { parameterExp.SetTargetExpInfo(ECSqlTypeInfo::CreatePrimitive(ECN::PRIMITIVETYPE_Long, true, EXTENDEDTYPENAME_Id)); return true; From c676b3a45bb81e170043c9141efa66b78070ab5b Mon Sep 17 00:00:00 2001 From: SohamBhattacharjee Date: Fri, 4 Oct 2024 19:06:38 +0530 Subject: [PATCH 19/43] Comments regarding constant name of IdSet table resolved --- iModelCore/ECDb/ECDb/BuiltInVTabs.h | 3 ++- iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp | 2 +- iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/iModelCore/ECDb/ECDb/BuiltInVTabs.h b/iModelCore/ECDb/ECDb/BuiltInVTabs.h index 9e0687d09b7..41dc2947cb1 100644 --- a/iModelCore/ECDb/ECDb/BuiltInVTabs.h +++ b/iModelCore/ECDb/ECDb/BuiltInVTabs.h @@ -44,6 +44,7 @@ struct ClassPropsModule : BeSQLite::DbModule { }; struct IdSetModule : ECDbModule { + constexpr static auto NAME = "IdSet"; struct IdSetTable : ECDbVirtualTable { struct IdSetCursor : ECDbCursor { @@ -80,7 +81,7 @@ struct IdSetModule : ECDbModule { public: IdSetModule(ECDbR db): ECDbModule( db, - "IdSet", + NAME, "CREATE TABLE x(id, json_array_ids hidden)", R"xml( ECSqlBinderFactory::CreateBinder(ECSqlPrepareContex if (const Exp* exp = parameterExp.FindParent(Exp::Type::MemberFunctionCall)) { if (MemberFunctionCallExp const* parentExp = exp->GetAsCP()) { - if (parentExp->IsTableValuedFunc() && parentExp->GetFunctionName().EqualsI("IdSet") && parentExp->GetChildren()[0] == ¶meterExp) + if (parentExp->IsTableValuedFunc() && parentExp->GetFunctionName().EqualsI(IdSetModule::NAME) && parentExp->GetChildren()[0] == ¶meterExp) { std::unique_ptr arrayECsqlBinder = CreateArrayECSqlBinder(ctx, parameterExp.GetTypeInfo(), paramNameGen); arrayECsqlBinder->GetBinderInfo().SetIfBinderIsForIdSetVirtualTable(true); diff --git a/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp b/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp index 0ba55c84c4e..097563469b2 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp @@ -817,7 +817,7 @@ Exp::FinalizeParseStatus MemberFunctionCallExp::_FinalizeParsing(ECSqlParseConte if (mode == Exp::FinalizeParseMode::AfterFinalizingChildren) { if (this->IsTableValuedFunc()) { - if(m_functionName.EqualsIAscii("IdSet")) + if(m_functionName.EqualsIAscii(IdSetModule::NAME)) { ValueExp const* argExp = GetArgument(0); if(argExp == nullptr) @@ -960,7 +960,7 @@ Utf8String MemberFunctionCallExp::_ToString() const //+---------------+---------------+---------------+---------------+---------------+------ bool MemberFunctionCallExp::_TryDetermineParameterExpType(ECSqlParseContext& ctx, ParameterExp& parameterExp) const { - if(this->IsTableValuedFunc() && m_functionName.EqualsIAscii("IdSet")) + if(this->IsTableValuedFunc() && m_functionName.EqualsIAscii(IdSetModule::NAME)) { parameterExp.SetTargetExpInfo(ECSqlTypeInfo::CreatePrimitive(ECN::PRIMITIVETYPE_Long, true, EXTENDEDTYPENAME_Id)); return true; From a04462c0550d2e673baf0244543a77f4ab0ee584 Mon Sep 17 00:00:00 2001 From: SohamBhattacharjee Date: Sat, 5 Oct 2024 20:13:20 +0530 Subject: [PATCH 20/43] binderInfo refactoring --- .../ECDb/ECDb/ConcurrentQueryManagerImpl.cpp | 4 +- .../ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp | 6 +-- iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.h | 4 +- iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp | 14 +++---- iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.h | 8 ++-- .../ECDb/ECDb/ECSql/ECSqlPreparedStatement.h | 4 +- .../ECDb/ECDb/ECSql/ECSqlStatementNoopImpls.h | 2 +- iModelCore/ECDb/ECDb/ECSql/IECSqlBinder.cpp | 2 +- iModelCore/ECDb/ECDb/ECSql/IdECSqlBinder.cpp | 10 ++++- iModelCore/ECDb/ECDb/ECSql/IdECSqlBinder.h | 3 ++ .../ECSql/NavigationPropertyECSqlBinder.cpp | 10 ++++- .../ECSql/NavigationPropertyECSqlBinder.h | 3 ++ .../ECDb/ECDb/ECSql/PointECSqlBinder.cpp | 8 ++++ iModelCore/ECDb/ECDb/ECSql/PointECSqlBinder.h | 5 ++- .../ECDb/ECDb/ECSql/PrimitiveECSqlBinder.cpp | 8 ++++ .../ECDb/ECDb/ECSql/PrimitiveECSqlBinder.h | 5 ++- .../ECDb/ECDb/ECSql/StructECSqlBinder.cpp | 10 ++++- .../ECDb/ECDb/ECSql/StructECSqlBinder.h | 3 ++ .../ECDb/ECDb/ECSql/VirtualSetBinder.cpp | 10 ++++- iModelCore/ECDb/ECDb/ECSql/VirtualSetBinder.h | 3 ++ iModelCore/ECDb/PublicAPI/ECDb/IECSqlBinder.h | 40 +++++++++---------- 21 files changed, 112 insertions(+), 50 deletions(-) diff --git a/iModelCore/ECDb/ECDb/ConcurrentQueryManagerImpl.cpp b/iModelCore/ECDb/ECDb/ConcurrentQueryManagerImpl.cpp index 3bd64b95b64..4b7a6724cef 100644 --- a/iModelCore/ECDb/ECDb/ConcurrentQueryManagerImpl.cpp +++ b/iModelCore/ECDb/ECDb/ConcurrentQueryManagerImpl.cpp @@ -1900,12 +1900,12 @@ bool ECSqlParams::TryBindTo(ECSqlStatement& stmt, std::string& err) const { st = stmt.BindId(index, param.GetValueId()); break; case ECSqlParam::Type::IdSet: { BinderInfo const& binderInfo = stmt.GetBinderInfo(index); - if(binderInfo.GetBinderType() == BinderInfo::BinderType::VirtualSetECSqlBinderType) + if(binderInfo.GetType() == BinderInfo::BinderType::VirtualSet) { std::shared_ptr> idSet = std::make_shared>(param.GetValueIdSet()); st = stmt.BindVirtualSet(index, idSet); } - else if(binderInfo.GetBinderType() == BinderInfo::BinderType::ArrayECSqlBinderType && binderInfo.CheckIfBinderIsForIdSetVirtualTable()) + else if(binderInfo.GetType() == BinderInfo::BinderType::Array && binderInfo.IsForIdSet()) { bool allElementsAdded = true; IECSqlBinder& binder = stmt.GetBinder(index); diff --git a/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp index c180e641051..853e0ddfa99 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp @@ -11,8 +11,8 @@ BEGIN_BENTLEY_SQLITE_EC_NAMESPACE //--------------------------------------------------------------------------------------- // @bsimethod //--------------------------------------------------------------------------------------- -ArrayECSqlBinder::ArrayECSqlBinder(ECSqlPrepareContext& ctx, ECSqlTypeInfo const& typeInfo, SqlParamNameGenerator& paramNameGen) - : ECSqlBinder(ctx, typeInfo, paramNameGen, 1, true, true, BinderInfo::BinderType::ArrayECSqlBinderType) +ArrayECSqlBinder::ArrayECSqlBinder(ECSqlPrepareContext& ctx, ECSqlTypeInfo const& typeInfo, SqlParamNameGenerator& paramNameGen, bool isForIdSet) + : ECSqlBinder(ctx, typeInfo, paramNameGen, 1, true, true), m_binderInfo(BinderInfo::BinderType::Array, isForIdSet) { BeAssert(GetTypeInfo().IsArray()); Initialize(); @@ -67,7 +67,7 @@ ECSqlStatus ArrayECSqlBinder::_OnBeforeStep() // @bsimethod //--------------------------------------------------------------------------------------- ArrayECSqlBinder::JsonValueBinder::JsonValueBinder(ECDbCR ecdb, ECSqlTypeInfo const& typeInfo, rapidjson::Value& json, rapidjson::MemoryPoolAllocator<>& jsonAllocator) - : IECSqlBinder(), m_ecdb(&ecdb), m_typeInfo(typeInfo), m_json(&json), m_jsonAllocator(&jsonAllocator), m_currentArrayElementBinder(nullptr), m_binderInfo(BinderInfo::BinderType::JsonValueBinderType) + : IECSqlBinder(), m_ecdb(&ecdb), m_typeInfo(typeInfo), m_json(&json), m_jsonAllocator(&jsonAllocator), m_currentArrayElementBinder(nullptr), m_binderInfo(BinderInfo::BinderType::JsonValue) { BeAssert(m_json != nullptr); BeAssert(m_jsonAllocator != nullptr); diff --git a/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.h b/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.h index 3e5d9e5af18..e9319d78624 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.h +++ b/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.h @@ -71,6 +71,7 @@ struct ArrayECSqlBinder final : public ECSqlBinder rapidjson::Document m_json; std::unique_ptr m_rootBinder = nullptr; + BinderInfo m_binderInfo; void Initialize(); @@ -95,9 +96,10 @@ struct ArrayECSqlBinder final : public ECSqlBinder IECSqlBinder& _BindStructMember(ECN::ECPropertyId structMemberPropertyId) override { return m_rootBinder->operator[](structMemberPropertyId); } IECSqlBinder& _AddArrayElement() override { return m_rootBinder->AddArrayElement(); } + BinderInfo const& _GetBinderInfo() override { return m_binderInfo; } public: - ArrayECSqlBinder(ECSqlPrepareContext&, ECSqlTypeInfo const&, SqlParamNameGenerator&); + ArrayECSqlBinder(ECSqlPrepareContext&, ECSqlTypeInfo const&, SqlParamNameGenerator&, bool); ~ArrayECSqlBinder() {} }; diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp index aba19b73197..bc735076f80 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp @@ -10,8 +10,8 @@ BEGIN_BENTLEY_SQLITE_EC_NAMESPACE //--------------------------------------------------------------------------------------- // @bsimethod //--------------------------------------------------------------------------------------- -ECSqlBinder::ECSqlBinder(ECSqlPrepareContext& ctx, ECSqlTypeInfo const& typeInfo, SqlParamNameGenerator& nameGen, int mappedSqlParameterCount, bool hasToCallOnBeforeStep, bool hasToCallOnClearBindings, BinderInfo::BinderType binderType) - : m_preparedStatement(ctx.GetPreparedStatement()), m_typeInfo(typeInfo), m_hasToCallOnBeforeStep(hasToCallOnBeforeStep), m_hasToCallOnClearBindings(hasToCallOnClearBindings), m_binderInfo(binderType) +ECSqlBinder::ECSqlBinder(ECSqlPrepareContext& ctx, ECSqlTypeInfo const& typeInfo, SqlParamNameGenerator& nameGen, int mappedSqlParameterCount, bool hasToCallOnBeforeStep, bool hasToCallOnClearBindings) + : m_preparedStatement(ctx.GetPreparedStatement()), m_typeInfo(typeInfo), m_hasToCallOnBeforeStep(hasToCallOnBeforeStep), m_hasToCallOnClearBindings(hasToCallOnClearBindings) { for (int i = 0; i < mappedSqlParameterCount; i++) { @@ -95,9 +95,7 @@ std::unique_ptr ECSqlBinderFactory::CreateBinder(ECSqlPrepareContex if (MemberFunctionCallExp const* parentExp = exp->GetAsCP()) { if (parentExp->IsTableValuedFunc() && parentExp->GetFunctionName().EqualsI(IdSetModule::NAME) && parentExp->GetChildren()[0] == ¶meterExp) { - std::unique_ptr arrayECsqlBinder = CreateArrayECSqlBinder(ctx, parameterExp.GetTypeInfo(), paramNameGen); - arrayECsqlBinder->GetBinderInfo().SetIfBinderIsForIdSetVirtualTable(true); - return arrayECsqlBinder; + return CreateArrayECSqlBinder(ctx, parameterExp.GetTypeInfo(), paramNameGen, true); } } } @@ -150,7 +148,7 @@ std::unique_ptr ECSqlBinderFactory::CreateBinder(ECSqlPrepareContex case ECSqlTypeInfo::Kind::PrimitiveArray: case ECSqlTypeInfo::Kind::StructArray: - return std::unique_ptr(new ArrayECSqlBinder(ctx, typeInfo, nameGen)); + return std::unique_ptr(new ArrayECSqlBinder(ctx, typeInfo, nameGen, false)); case ECSqlTypeInfo::Kind::Navigation: return std::unique_ptr(new NavigationPropertyECSqlBinder(ctx, typeInfo, nameGen)); @@ -194,9 +192,9 @@ std::unique_ptr ECSqlBinderFactory::CreateVirtualSetBinder(ECS //--------------------------------------------------------------------------------------- // @bsimethod //--------------------------------------------------------------------------------------- -std::unique_ptr ECSqlBinderFactory::CreateArrayECSqlBinder(ECSqlPrepareContext& ctx, ECSqlTypeInfo const& typeInfo, ECSqlBinder::SqlParamNameGenerator& paramNameGen) +std::unique_ptr ECSqlBinderFactory::CreateArrayECSqlBinder(ECSqlPrepareContext& ctx, ECSqlTypeInfo const& typeInfo, ECSqlBinder::SqlParamNameGenerator& paramNameGen, bool isForIdSet) { - return std::unique_ptr(new ArrayECSqlBinder(ctx, typeInfo, paramNameGen)); + return std::unique_ptr(new ArrayECSqlBinder(ctx, typeInfo, paramNameGen, isForIdSet)); } //--------------------------------------------------------------------------------------- diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.h b/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.h index 763755bcd16..4eaaa48c0be 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.h +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.h @@ -58,16 +58,14 @@ struct ECSqlBinder : IECSqlBinder std::vector m_mappedSqlParameterNames; bool m_hasToCallOnBeforeStep = false; bool m_hasToCallOnClearBindings = false; - BinderInfo m_binderInfo; virtual ECSqlStatus _OnBeforeStep() { return ECSqlStatus::Success; } virtual void _OnClearBindings() {} - BinderInfo& _GetBinderInfo() override { return m_binderInfo; }; protected: - ECSqlBinder(ECSqlPrepareContext&, ECSqlTypeInfo const&, SqlParamNameGenerator&, int mappedSqlParameterCount, bool hasToCallOnBeforeStep, bool hasToCallOnClearBindings, BinderInfo::BinderType binderType); + ECSqlBinder(ECSqlPrepareContext&, ECSqlTypeInfo const&, SqlParamNameGenerator&, int mappedSqlParameterCount, bool hasToCallOnBeforeStep, bool hasToCallOnClearBindings); //! Use this ctor for compound binders where the mapped sql parameter count depends on its member binders - ECSqlBinder(ECSqlPrepareContext& ctx, ECSqlTypeInfo const& typeInfo, SqlParamNameGenerator& paramNameGen, bool hasToCallOnBeforeStep, bool hasToCallOnClearBindings, BinderInfo::BinderType binderType) : ECSqlBinder(ctx, typeInfo, paramNameGen, -1, hasToCallOnBeforeStep, hasToCallOnClearBindings, binderType) {} + ECSqlBinder(ECSqlPrepareContext& ctx, ECSqlTypeInfo const& typeInfo, SqlParamNameGenerator& paramNameGen, bool hasToCallOnBeforeStep, bool hasToCallOnClearBindings) : ECSqlBinder(ctx, typeInfo, paramNameGen, -1, hasToCallOnBeforeStep, hasToCallOnClearBindings) {} void AddChildMemberMappedSqlParameterIndices(ECSqlBinder const& memberBinder) { @@ -117,7 +115,7 @@ struct ECSqlBinderFactory final static std::unique_ptr CreateIdBinder(ECSqlPrepareContext&, PropertyMap const&, ECSqlSystemPropertyInfo const&, ECSqlBinder::SqlParamNameGenerator&); static std::unique_ptr CreateIdBinderForQuery(ECSqlPrepareContext&, ECSqlTypeInfo const&, ECSqlBinder::SqlParamNameGenerator&); static std::unique_ptr CreateVirtualSetBinder(ECSqlPrepareContext&, ECSqlTypeInfo const&, ECSqlBinder::SqlParamNameGenerator&); - static std::unique_ptr CreateArrayECSqlBinder(ECSqlPrepareContext&, ECSqlTypeInfo const&, ECSqlBinder::SqlParamNameGenerator&); + static std::unique_ptr CreateArrayECSqlBinder(ECSqlPrepareContext&, ECSqlTypeInfo const&, ECSqlBinder::SqlParamNameGenerator&, bool); }; //======================================================================================= diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h index 6729a5eb6aa..ff5b02b7183 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h @@ -176,7 +176,7 @@ struct CompoundECSqlPreparedStatement : IECSqlPreparedStatement void _AddBinder(IECSqlBinder& binder) override { BeAssert(m_idBinder == nullptr); m_idBinder = &binder; } public: - ProxyECInstanceIdECSqlBinder() : IProxyECSqlBinder(), m_binderInfo(BinderInfo::BinderType::ProxyECInstanceIdECSqlBinderType) {} + ProxyECInstanceIdECSqlBinder() : IProxyECSqlBinder(), m_binderInfo(BinderInfo::BinderType::ProxyECInstanceId) {} IECSqlBinder& GetBinder() { BeAssert(m_idBinder != nullptr); return *m_idBinder; } @@ -219,7 +219,7 @@ struct CompoundECSqlPreparedStatement : IECSqlPreparedStatement void _AddBinder(IECSqlBinder& binder) override { m_binders.push_back(&binder); } public: - ProxyECSqlBinder() : IProxyECSqlBinder(),m_binderInfo(BinderInfo::BinderType::ProxyECSqlBinderType) {} + ProxyECSqlBinder() : IProxyECSqlBinder(),m_binderInfo(BinderInfo::BinderType::Proxy) {} }; diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlStatementNoopImpls.h b/iModelCore/ECDb/ECDb/ECSql/ECSqlStatementNoopImpls.h index 84ff9197359..a48c004c42c 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlStatementNoopImpls.h +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlStatementNoopImpls.h @@ -19,7 +19,7 @@ struct NoopECSqlBinder final : public IECSqlBinder static NoopECSqlBinder* s_singleton; BinderInfo m_binderInfo; - NoopECSqlBinder() : m_errorStatus(ECSqlStatus::Error), m_binderInfo(BinderInfo::BinderType::NoopECSqlBinderType) {} + NoopECSqlBinder() : m_errorStatus(ECSqlStatus::Error), m_binderInfo(BinderInfo::BinderType::Noop) {} ECSqlStatus _BindNull() override { return m_errorStatus; } ECSqlStatus _BindBoolean(bool value) override { return m_errorStatus; } diff --git a/iModelCore/ECDb/ECDb/ECSql/IECSqlBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/IECSqlBinder.cpp index af83fdb220d..ca4850070d2 100644 --- a/iModelCore/ECDb/ECDb/ECSql/IECSqlBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/IECSqlBinder.cpp @@ -155,6 +155,6 @@ IECSqlBinder& IECSqlBinder::AddArrayElement() { return _AddArrayElement(); } //--------------------------------------------------------------------------------------- // @bsimethod //--------------------------------------------------------------------------------------- -BinderInfo& IECSqlBinder::GetBinderInfo() { return _GetBinderInfo(); } +BinderInfo const& IECSqlBinder::GetBinderInfo() { return _GetBinderInfo(); } END_BENTLEY_SQLITE_EC_NAMESPACE \ No newline at end of file diff --git a/iModelCore/ECDb/ECDb/ECSql/IdECSqlBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/IdECSqlBinder.cpp index f1d131bbf29..0257a8a7314 100644 --- a/iModelCore/ECDb/ECDb/ECSql/IdECSqlBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/IdECSqlBinder.cpp @@ -12,7 +12,7 @@ BEGIN_BENTLEY_SQLITE_EC_NAMESPACE // @bsimethod //--------------------------------------------------------------------------------------- IdECSqlBinder::IdECSqlBinder(ECSqlPrepareContext& ctx, ECSqlTypeInfo const& typeInfo, bool isNoop, SqlParamNameGenerator& paramNameGen) - : ECSqlBinder(ctx, typeInfo, paramNameGen, isNoop ? 0 : 1, false, false,BinderInfo::BinderType::IdECSqlBinderType), m_isNoop(isNoop) + : ECSqlBinder(ctx, typeInfo, paramNameGen, isNoop ? 0 : 1, false, false), m_isNoop(isNoop), m_binderInfo(BinderInfo::BinderType::Id) {} //--------------------------------------------------------------------------------------- @@ -196,4 +196,12 @@ IECSqlBinder& IdECSqlBinder::_AddArrayElement() return NoopECSqlBinder::Get(); } +//--------------------------------------------------------------------------------------- +// @bsimethod +//--------------------------------------------------------------------------------------- +BinderInfo const& IdECSqlBinder::_GetBinderInfo() + { + return m_binderInfo; + } + END_BENTLEY_SQLITE_EC_NAMESPACE diff --git a/iModelCore/ECDb/ECDb/ECSql/IdECSqlBinder.h b/iModelCore/ECDb/ECDb/ECSql/IdECSqlBinder.h index f1df4d6b5a4..f703eecbc20 100644 --- a/iModelCore/ECDb/ECDb/ECSql/IdECSqlBinder.h +++ b/iModelCore/ECDb/ECDb/ECSql/IdECSqlBinder.h @@ -15,6 +15,7 @@ struct IdECSqlBinder final : public ECSqlBinder { private: bool m_isNoop; + BinderInfo m_binderInfo; int GetSqlParamIndex() const { @@ -45,6 +46,8 @@ struct IdECSqlBinder final : public ECSqlBinder IECSqlBinder& _AddArrayElement() override; + BinderInfo const& _GetBinderInfo() override; + public: IdECSqlBinder(ECSqlPrepareContext&, ECSqlTypeInfo const&, bool isNoop, SqlParamNameGenerator&); ~IdECSqlBinder() { OnClearBindings(); } diff --git a/iModelCore/ECDb/ECDb/ECSql/NavigationPropertyECSqlBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/NavigationPropertyECSqlBinder.cpp index 3570f46612c..d2a0718e152 100644 --- a/iModelCore/ECDb/ECDb/ECSql/NavigationPropertyECSqlBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/NavigationPropertyECSqlBinder.cpp @@ -11,7 +11,7 @@ BEGIN_BENTLEY_SQLITE_EC_NAMESPACE // @bsimethod //--------------------------------------------------------------------------------------- NavigationPropertyECSqlBinder::NavigationPropertyECSqlBinder(ECSqlPrepareContext& ctx, ECSqlTypeInfo const& ecsqlTypeInfo, SqlParamNameGenerator& paramNameGen) - : ECSqlBinder(ctx, ecsqlTypeInfo, paramNameGen, true, false, BinderInfo::BinderType::NavigationPropertyECSqlBinderType) + : ECSqlBinder(ctx, ecsqlTypeInfo, paramNameGen, true, false), m_binderInfo(BinderInfo::BinderType::NavigationProperty) { Initialize(ctx, paramNameGen); } @@ -216,4 +216,12 @@ IECSqlBinder& NavigationPropertyECSqlBinder::_AddArrayElement() LOG.error("Type mismatch. Cannot bind array to navigation property parameter."); return NoopECSqlBinder::Get(); } + +//--------------------------------------------------------------------------------------- +// @bsimethod +//--------------------------------------------------------------------------------------- +BinderInfo const& NavigationPropertyECSqlBinder::_GetBinderInfo() + { + return m_binderInfo; + } END_BENTLEY_SQLITE_EC_NAMESPACE diff --git a/iModelCore/ECDb/ECDb/ECSql/NavigationPropertyECSqlBinder.h b/iModelCore/ECDb/ECDb/ECSql/NavigationPropertyECSqlBinder.h index 22e944f3c25..6cd1db795a3 100644 --- a/iModelCore/ECDb/ECDb/ECSql/NavigationPropertyECSqlBinder.h +++ b/iModelCore/ECDb/ECDb/ECSql/NavigationPropertyECSqlBinder.h @@ -18,6 +18,7 @@ struct NavigationPropertyECSqlBinder final : public ECSqlBinder private: std::unique_ptr m_idBinder = nullptr; std::unique_ptr m_relECClassIdBinder = nullptr; + BinderInfo m_binderInfo; NavigationPropertyECSqlBinder(ECSqlPrepareContext&, ECSqlTypeInfo const&, SqlParamNameGenerator&); BentleyStatus Initialize(ECSqlPrepareContext&, SqlParamNameGenerator&); @@ -41,6 +42,8 @@ struct NavigationPropertyECSqlBinder final : public ECSqlBinder IECSqlBinder& _AddArrayElement() override; + BinderInfo const& _GetBinderInfo() override; + public: ~NavigationPropertyECSqlBinder() {} }; diff --git a/iModelCore/ECDb/ECDb/ECSql/PointECSqlBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/PointECSqlBinder.cpp index cf1a51f4b60..0a99d37a97c 100644 --- a/iModelCore/ECDb/ECDb/ECSql/PointECSqlBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/PointECSqlBinder.cpp @@ -189,4 +189,12 @@ IECSqlBinder& PointECSqlBinder::_AddArrayElement() return NoopECSqlBinder::Get(); } +//--------------------------------------------------------------------------------------- +// @bsimethod +//--------------------------------------------------------------------------------------- +BinderInfo const& PointECSqlBinder::_GetBinderInfo() + { + return m_binderInfo; + } + END_BENTLEY_SQLITE_EC_NAMESPACE diff --git a/iModelCore/ECDb/ECDb/ECSql/PointECSqlBinder.h b/iModelCore/ECDb/ECDb/ECSql/PointECSqlBinder.h index a5ed1e18bb9..9929e60e61b 100644 --- a/iModelCore/ECDb/ECDb/ECSql/PointECSqlBinder.h +++ b/iModelCore/ECDb/ECDb/ECSql/PointECSqlBinder.h @@ -21,6 +21,7 @@ struct PointECSqlBinder final : public ECSqlBinder }; bool m_isPoint3d; + BinderInfo m_binderInfo; ECSqlStatus _BindNull() override; ECSqlStatus _BindBoolean(bool value) override; @@ -41,6 +42,8 @@ struct PointECSqlBinder final : public ECSqlBinder IECSqlBinder& _AddArrayElement() override; + BinderInfo const& _GetBinderInfo() override; + int GetCoordSqlParamIndex(Coordinate coord) const { BeAssert(GetMappedSqlParameterNames().size() == (m_isPoint3d ? 3 : 2)); @@ -53,7 +56,7 @@ struct PointECSqlBinder final : public ECSqlBinder public: PointECSqlBinder(ECSqlPrepareContext& ctx, ECSqlTypeInfo const& typeInfo, bool isPoint3d, SqlParamNameGenerator& paramNameGen) - : ECSqlBinder(ctx, typeInfo, paramNameGen, isPoint3d ? 3 : 2, false, false, BinderInfo::BinderType::PointECSqlBinderType), m_isPoint3d(isPoint3d) + : ECSqlBinder(ctx, typeInfo, paramNameGen, isPoint3d ? 3 : 2, false, false), m_isPoint3d(isPoint3d), m_binderInfo(BinderInfo::BinderType::Point) {} ~PointECSqlBinder() {} diff --git a/iModelCore/ECDb/ECDb/ECSql/PrimitiveECSqlBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/PrimitiveECSqlBinder.cpp index 8ed67ea4ff8..5dadccbc9a2 100644 --- a/iModelCore/ECDb/ECDb/ECSql/PrimitiveECSqlBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/PrimitiveECSqlBinder.cpp @@ -264,6 +264,14 @@ IECSqlBinder& PrimitiveECSqlBinder::_AddArrayElement() return NoopECSqlBinder::Get(); } +//--------------------------------------------------------------------------------------- +// @bsimethod +//--------------------------------------------------------------------------------------- +BinderInfo const& PrimitiveECSqlBinder::_GetBinderInfo() + { + return m_binderInfo; + } + //--------------------------------------------------------------------------------------- // @bsimethod //--------------------------------------------------------------------------------------- diff --git a/iModelCore/ECDb/ECDb/ECSql/PrimitiveECSqlBinder.h b/iModelCore/ECDb/ECDb/ECSql/PrimitiveECSqlBinder.h index fc07109aa96..014e7a9b0ba 100644 --- a/iModelCore/ECDb/ECDb/ECSql/PrimitiveECSqlBinder.h +++ b/iModelCore/ECDb/ECDb/ECSql/PrimitiveECSqlBinder.h @@ -14,6 +14,8 @@ BEGIN_BENTLEY_SQLITE_EC_NAMESPACE struct PrimitiveECSqlBinder final : public ECSqlBinder { private: + BinderInfo m_binderInfo; + ECSqlStatus CanBind(ECN::PrimitiveType requestedType) const; ECSqlStatus _BindNull() override; @@ -34,6 +36,7 @@ struct PrimitiveECSqlBinder final : public ECSqlBinder IECSqlBinder& _BindStructMember(ECN::ECPropertyId structMemberPropertyId) override; IECSqlBinder& _AddArrayElement() override; + BinderInfo const& _GetBinderInfo() override; int GetSqlParameterIndex() const { @@ -43,7 +46,7 @@ struct PrimitiveECSqlBinder final : public ECSqlBinder } public: - PrimitiveECSqlBinder(ECSqlPrepareContext& ctx, ECSqlTypeInfo const& typeInfo, SqlParamNameGenerator& paramNameGen) : ECSqlBinder(ctx, typeInfo, paramNameGen, 1, false, false, BinderInfo::BinderType::PrimitiveECSqlBinderType) {} + PrimitiveECSqlBinder(ECSqlPrepareContext& ctx, ECSqlTypeInfo const& typeInfo, SqlParamNameGenerator& paramNameGen) : ECSqlBinder(ctx, typeInfo, paramNameGen, 1, false, false), m_binderInfo(BinderInfo::BinderType::Primitive) {} ~PrimitiveECSqlBinder() { OnClearBindings(); } }; diff --git a/iModelCore/ECDb/ECDb/ECSql/StructECSqlBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/StructECSqlBinder.cpp index afc24cf2401..31bfae77761 100644 --- a/iModelCore/ECDb/ECDb/ECSql/StructECSqlBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/StructECSqlBinder.cpp @@ -10,7 +10,7 @@ BEGIN_BENTLEY_SQLITE_EC_NAMESPACE // @bsimethod //--------------------------------------------------------------------------------------- StructECSqlBinder::StructECSqlBinder(ECSqlPrepareContext& ctx, ECSqlTypeInfo const& ecsqlTypeInfo, SqlParamNameGenerator& paramNameGen) - : ECSqlBinder(ctx, ecsqlTypeInfo, paramNameGen, true, true, BinderInfo::BinderType::StructECSqlBinderType) + : ECSqlBinder(ctx, ecsqlTypeInfo, paramNameGen, true, true), m_binderInfo( BinderInfo::BinderType::Struct) { Initialize(ctx, paramNameGen); } @@ -229,4 +229,12 @@ IECSqlBinder& StructECSqlBinder::_AddArrayElement() LOG.error("Type mismatch. Cannot bind array to struct parameter."); return NoopECSqlBinder::Get(); } + +//--------------------------------------------------------------------------------------- +// @bsimethod +//--------------------------------------------------------------------------------------- +BinderInfo const& StructECSqlBinder::_GetBinderInfo() + { + return m_binderInfo; + } END_BENTLEY_SQLITE_EC_NAMESPACE \ No newline at end of file diff --git a/iModelCore/ECDb/ECDb/ECSql/StructECSqlBinder.h b/iModelCore/ECDb/ECDb/ECSql/StructECSqlBinder.h index f15f0878e62..f48973a2d30 100644 --- a/iModelCore/ECDb/ECDb/ECSql/StructECSqlBinder.h +++ b/iModelCore/ECDb/ECDb/ECSql/StructECSqlBinder.h @@ -15,6 +15,7 @@ struct StructECSqlBinder final : public ECSqlBinder private: std::map> m_memberBinders; + BinderInfo m_binderInfo; StructECSqlBinder(ECSqlPrepareContext&, ECSqlTypeInfo const&, SqlParamNameGenerator&); BentleyStatus Initialize(ECSqlPrepareContext&, SqlParamNameGenerator&); @@ -41,6 +42,8 @@ struct StructECSqlBinder final : public ECSqlBinder IECSqlBinder& _AddArrayElement() override; + BinderInfo const& _GetBinderInfo() override; + public: ~StructECSqlBinder() {} }; diff --git a/iModelCore/ECDb/ECDb/ECSql/VirtualSetBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/VirtualSetBinder.cpp index 664f44b72f0..0f3db369bce 100644 --- a/iModelCore/ECDb/ECDb/ECSql/VirtualSetBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/VirtualSetBinder.cpp @@ -12,7 +12,7 @@ BEGIN_BENTLEY_SQLITE_EC_NAMESPACE // @bsimethod //--------------------------------------------------------------------------------------- VirtualSetBinder::VirtualSetBinder(ECSqlPrepareContext& ctx, ECSqlTypeInfo const& typeInfo, SqlParamNameGenerator& paramNameGen) - : ECSqlBinder(ctx, typeInfo, paramNameGen, 1, false, false, BinderInfo::BinderType::VirtualSetECSqlBinderType) + : ECSqlBinder(ctx, typeInfo, paramNameGen, 1, false, false), m_binderInfo(BinderInfo::BinderType::VirtualSet) {} //--------------------------------------------------------------------------------------- @@ -156,6 +156,14 @@ IECSqlBinder& VirtualSetBinder::_AddArrayElement() return NoopECSqlBinder::Get(); } +//--------------------------------------------------------------------------------------- +// @bsimethod +//--------------------------------------------------------------------------------------- +BinderInfo const& VirtualSetBinder::_GetBinderInfo() + { + return m_binderInfo; + } + // -------------------------------------------------------------------------------------- // @bsimethod //--------------------------------------------------------------------------------------- diff --git a/iModelCore/ECDb/ECDb/ECSql/VirtualSetBinder.h b/iModelCore/ECDb/ECDb/ECSql/VirtualSetBinder.h index 17fa3952255..addc89fe3e9 100644 --- a/iModelCore/ECDb/ECDb/ECSql/VirtualSetBinder.h +++ b/iModelCore/ECDb/ECDb/ECSql/VirtualSetBinder.h @@ -15,6 +15,7 @@ struct VirtualSetBinder final : public ECSqlBinder { private: std::shared_ptr m_virtualSet; + BinderInfo m_binderInfo; void _OnClearBindings() override { m_virtualSet.reset(); } @@ -44,6 +45,8 @@ struct VirtualSetBinder final : public ECSqlBinder IECSqlBinder& _AddArrayElement() override; + BinderInfo const& _GetBinderInfo() override; + public: VirtualSetBinder(ECSqlPrepareContext&, ECSqlTypeInfo const&, SqlParamNameGenerator&); ~VirtualSetBinder() { OnClearBindings(); }; diff --git a/iModelCore/ECDb/PublicAPI/ECDb/IECSqlBinder.h b/iModelCore/ECDb/PublicAPI/ECDb/IECSqlBinder.h index e2eb02ec2a3..84af4bd72db 100644 --- a/iModelCore/ECDb/PublicAPI/ECDb/IECSqlBinder.h +++ b/iModelCore/ECDb/PublicAPI/ECDb/IECSqlBinder.h @@ -18,29 +18,27 @@ struct BinderInfo final public: enum class BinderType { - ArrayECSqlBinderType, - IdECSqlBinderType, - VirtualSetECSqlBinderType, - NavigationPropertyECSqlBinderType, - PointECSqlBinderType, - PrimitiveECSqlBinderType, - StructECSqlBinderType, - JsonValueBinderType, - NoopECSqlBinderType, - ProxyECInstanceIdECSqlBinderType, - ProxyECSqlBinderType, - NotSpecified + Array, + Id, + VirtualSet, + NavigationProperty, + Point, + Primitive, + Struct, + JsonValue, + Noop, + ProxyECInstanceId, + Proxy, }; private: - BinderType m_binderType = BinderType::NotSpecified; - bool m_binderIsForIdSetVirtualTable = false; + BinderType m_binderType ; + bool m_binderIsForIdSet = false; public: - BinderInfo(BinderType binderType) : m_binderType(binderType), m_binderIsForIdSetVirtualTable(false){} - BinderInfo(BinderType binderType, bool binderIsForInVirtualSetOrIdSetVirtualTable) : m_binderType(binderType), m_binderIsForIdSetVirtualTable(binderIsForInVirtualSetOrIdSetVirtualTable){} - BinderInfo::BinderType GetBinderType() const { return m_binderType; } - bool CheckIfBinderIsForIdSetVirtualTable() const { return m_binderIsForIdSetVirtualTable; } - void SetIfBinderIsForIdSetVirtualTable(bool val) { m_binderIsForIdSetVirtualTable = val; } + explicit BinderInfo(BinderType binderType) : m_binderType(binderType), m_binderIsForIdSet(false){} + BinderInfo(BinderType binderType, bool binderIsForInVirtualSetOrIdSetVirtualTable) : m_binderType(binderType), m_binderIsForIdSet(binderIsForInVirtualSetOrIdSetVirtualTable){} + BinderType GetType() const { return m_binderType; } + bool IsForIdSet() const { return m_binderIsForIdSet; } }; @@ -90,7 +88,7 @@ struct EXPORT_VTABLE_ATTRIBUTE IECSqlBinder virtual IECSqlBinder& _BindStructMember(ECN::ECPropertyId structMemberPropertyId) = 0; virtual IECSqlBinder& _AddArrayElement() = 0; - virtual BinderInfo& _GetBinderInfo() = 0; + virtual BinderInfo const& _GetBinderInfo() = 0; protected: @@ -236,7 +234,7 @@ struct EXPORT_VTABLE_ATTRIBUTE IECSqlBinder ECDB_EXPORT IECSqlBinder& AddArrayElement(); //! @return Gets the BinderInfo for the specific binder - ECDB_EXPORT BinderInfo& GetBinderInfo(); + ECDB_EXPORT BinderInfo const& GetBinderInfo(); }; END_BENTLEY_SQLITE_EC_NAMESPACE From 59b16627a2746fe00723ff66b5b70c135cf55e77 Mon Sep 17 00:00:00 2001 From: SohamBhattacharjee Date: Sat, 5 Oct 2024 21:36:34 +0530 Subject: [PATCH 21/43] added flag to call _onbeforefirststep() once in PragmaECSQLStatement and renamed _OnBeforeStep() to _ONBeforeFirstStep() --- iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp | 2 +- iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.h | 2 +- iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp | 4 ++-- iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.h | 6 +++--- iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp | 4 ++-- iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h | 1 - .../ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.cpp | 10 ++++++++-- .../ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.h | 1 + iModelCore/ECDb/ECDb/ECSql/StructECSqlBinder.cpp | 4 ++-- iModelCore/ECDb/ECDb/ECSql/StructECSqlBinder.h | 2 +- 10 files changed, 21 insertions(+), 15 deletions(-) diff --git a/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp index 853e0ddfa99..875b6818b23 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp @@ -34,7 +34,7 @@ void ArrayECSqlBinder::Initialize() //--------------------------------------------------------------------------------------- // @bsimethod //--------------------------------------------------------------------------------------- -ECSqlStatus ArrayECSqlBinder::_OnBeforeStep() +ECSqlStatus ArrayECSqlBinder::_OnBeforeFirstStep() { const uint32_t arrayLength = m_json.IsNull() ? 0 : (uint32_t) m_json.Size(); // from the API we cannot tell between binding NULL and binding an empty array. so we treat them diff --git a/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.h b/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.h index e9319d78624..75b581c2a83 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.h +++ b/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.h @@ -76,7 +76,7 @@ struct ArrayECSqlBinder final : public ECSqlBinder void Initialize(); void _OnClearBindings() override { Initialize(); } - ECSqlStatus _OnBeforeStep() override; + ECSqlStatus _OnBeforeFirstStep() override; ECSqlStatus _BindNull() override { _OnClearBindings(); return ECSqlStatus::Success; } ECSqlStatus _BindBoolean(bool value) override { return m_rootBinder->BindBoolean(value); } diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp index bc735076f80..10b5b61417d 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.cpp @@ -335,11 +335,11 @@ ECSqlBinder* ECSqlParameterMap::AddBinder(ECSqlPrepareContext& ctx, ParameterExp //--------------------------------------------------------------------------------------- // @bsimethod //--------------------------------------------------------------------------------------- -ECSqlStatus ECSqlParameterMap::OnBeforeStep() +ECSqlStatus ECSqlParameterMap::OnBeforeFirstStep() { for (ECSqlBinder* binder : m_bindersToCallOnStep) { - ECSqlStatus stat = binder->OnBeforeStep(); + ECSqlStatus stat = binder->OnBeforeFirstStep(); if (!stat.IsSuccess()) return stat; } diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.h b/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.h index 4eaaa48c0be..c06a3d606fc 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.h +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlBinder.h @@ -59,7 +59,7 @@ struct ECSqlBinder : IECSqlBinder bool m_hasToCallOnBeforeStep = false; bool m_hasToCallOnClearBindings = false; - virtual ECSqlStatus _OnBeforeStep() { return ECSqlStatus::Success; } + virtual ECSqlStatus _OnBeforeFirstStep() { return ECSqlStatus::Success; } virtual void _OnClearBindings() {} protected: @@ -89,7 +89,7 @@ struct ECSqlBinder : IECSqlBinder ECSqlTypeInfo const& GetTypeInfo() const { return m_typeInfo; } - ECSqlStatus OnBeforeStep() { return _OnBeforeStep(); } + ECSqlStatus OnBeforeFirstStep() { return _OnBeforeFirstStep(); } void OnClearBindings() { return _OnClearBindings(); } }; @@ -150,7 +150,7 @@ struct ECSqlParameterMap final int GetIndexForName(Utf8StringCR ecsqlParameterName) const; ECSqlBinder* AddBinder(ECSqlPrepareContext&, ParameterExp const&); - ECSqlStatus OnBeforeStep(); + ECSqlStatus OnBeforeFirstStep(); //Bindings in SQLite have already been cleared at this point. The method //allows subclasses to clean-up additional resources tied to binding parameters diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp index 9dcf2619368..20791c48807 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp @@ -211,9 +211,9 @@ DbResult SingleECSqlPreparedStatement::DoStep() if (SUCCESS != AssertIsValid()) return BE_SQLITE_ERROR; - if(GetOnBeforeFirstStepNotCalled()) + if(m_onBeforeFirstStepNotCalled) { - if (!m_parameterMap.OnBeforeStep().IsSuccess()) + if (!m_parameterMap.OnBeforeFirstStep().IsSuccess()) return BE_SQLITE_ERROR; m_onBeforeFirstStepNotCalled = false; } diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h index ff5b02b7183..0179883f7f3 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h @@ -103,7 +103,6 @@ struct SingleECSqlPreparedStatement : IECSqlPreparedStatement ECSqlParameterMap const& GetParameterMap() const { return m_parameterMap; } ECSqlParameterMap& GetParameterMapR() { return m_parameterMap; } BeSQLite::Statement& GetSqliteStatement() { return m_sqliteStatement; } - bool GetOnBeforeFirstStepNotCalled() { return m_onBeforeFirstStepNotCalled; } }; //======================================================================================= diff --git a/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.cpp b/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.cpp index 1f908853a1a..32a75e76e89 100644 --- a/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.cpp @@ -605,6 +605,7 @@ ECSqlStatus PragmaECSqlPreparedStatement::_Reset() { if (rc != BE_SQLITE_OK) return ECSqlStatus(rc); + m_onBeforeFirstStepNotCalled = true; return ECSqlStatus::Success; } //--------------------------------------------------------------------------------------- @@ -634,8 +635,13 @@ DbResult PragmaECSqlPreparedStatement::DoStep() { if (SUCCESS != AssertIsValid()) return BE_SQLITE_ERROR; - if (!m_parameterMap.OnBeforeStep().IsSuccess()) - return BE_SQLITE_ERROR; + if(m_onBeforeFirstStepNotCalled) + { + if (!m_parameterMap.OnBeforeFirstStep().IsSuccess()) + return BE_SQLITE_ERROR; + m_onBeforeFirstStepNotCalled = false; + } + return m_resultSet->Step(); } diff --git a/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.h b/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.h index 68d3ac3340c..91cac82aa50 100644 --- a/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.h +++ b/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.h @@ -205,6 +205,7 @@ struct PragmaECSqlPreparedStatement final: public IECSqlPreparedStatement { private: ECSqlParameterMap m_parameterMap; std::unique_ptr m_resultSet; + bool m_onBeforeFirstStepNotCalled = true; IECSqlBinder& _GetBinder(int parameterIndex) const override; int _GetParameterIndex(Utf8CP parameterName) const override; int _TryGetParameterIndex(Utf8CP parameterName) const override; diff --git a/iModelCore/ECDb/ECDb/ECSql/StructECSqlBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/StructECSqlBinder.cpp index 31bfae77761..cfd8ad2ca83 100644 --- a/iModelCore/ECDb/ECDb/ECSql/StructECSqlBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/StructECSqlBinder.cpp @@ -43,11 +43,11 @@ BentleyStatus StructECSqlBinder::Initialize(ECSqlPrepareContext& ctx, SqlParamNa //--------------------------------------------------------------------------------------- // @bsimethod //--------------------------------------------------------------------------------------- -ECSqlStatus StructECSqlBinder::_OnBeforeStep() +ECSqlStatus StructECSqlBinder::_OnBeforeFirstStep() { for (auto const& kvPair : m_memberBinders) { - auto stat = kvPair.second->OnBeforeStep(); + auto stat = kvPair.second->OnBeforeFirstStep(); if (!stat.IsSuccess()) return stat; } diff --git a/iModelCore/ECDb/ECDb/ECSql/StructECSqlBinder.h b/iModelCore/ECDb/ECDb/ECSql/StructECSqlBinder.h index f48973a2d30..ad2dd3f55d8 100644 --- a/iModelCore/ECDb/ECDb/ECSql/StructECSqlBinder.h +++ b/iModelCore/ECDb/ECDb/ECSql/StructECSqlBinder.h @@ -21,7 +21,7 @@ struct StructECSqlBinder final : public ECSqlBinder BentleyStatus Initialize(ECSqlPrepareContext&, SqlParamNameGenerator&); void _OnClearBindings() override; - ECSqlStatus _OnBeforeStep() override; + ECSqlStatus _OnBeforeFirstStep() override; ECSqlStatus _BindNull() override; ECSqlStatus _BindBoolean(bool value) override; From 69d337c81397f1df66bfd9d0cb8aad3fc1f29843 Mon Sep 17 00:00:00 2001 From: SohamBhattacharjee Date: Sat, 5 Oct 2024 22:36:53 +0530 Subject: [PATCH 22/43] changes as per suggestions by Affan --- iModelCore/ECDb/ECDb/BuiltInVTabs.cpp | 49 +++---- iModelCore/ECDb/ECDb/BuiltInVTabs.h | 1 - .../ECDbIdSetVirtualTableTests.cpp | 131 ++++++++++++++---- 3 files changed, 119 insertions(+), 62 deletions(-) diff --git a/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp b/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp index 0e154e055c7..a5c65b09868 100644 --- a/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp +++ b/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp @@ -209,19 +209,20 @@ DbResult IdSetModule::IdSetTable::IdSetCursor::FilterJSONBasedOnType(BeJsConst& } else if(val.isNumeric()) { - if(val.asUInt64(-1) == -1) + uint64_t id = val.GetUInt64(); + if(id == 0) return BE_SQLITE_ERROR; - else - m_idSet.insert(val.asUInt64(-1)); + m_idSet.insert(id); } else if(val.isString()) { - if(val.asString().EqualsIAscii("")) + uint64_t id; + BentleyStatus status = BeStringUtilities::ParseUInt64(id, val.ToUtf8CP()); + if(status != BentleyStatus::SUCCESS) return BE_SQLITE_ERROR; - else if(val.asUInt64(-1) == -1) + if(id == 0) return BE_SQLITE_ERROR; - else - m_idSet.insert(val.asUInt64(-1)); + m_idSet.insert(id); } else return BE_SQLITE_ERROR; @@ -234,44 +235,28 @@ DbResult IdSetModule::IdSetTable::IdSetCursor::FilterJSONBasedOnType(BeJsConst& DbResult IdSetModule::IdSetTable::IdSetCursor::Filter(int idxNum, const char *idxStr, int argc, DbValue* argv) { int recompute = false; if( idxNum & 1 ){ - m_ArgType = argv[0].GetValueType(); - if(m_ArgType == DbValueType::TextVal) - { + if(argv[0].GetValueType() == DbValueType::TextVal) { Utf8String valueGiven = argv[0].GetValueText(); - if(valueGiven.EqualsIAscii("")) - { - Reset(); - } - else if(!valueGiven.EqualsIAscii(m_text)) - { + if(!valueGiven.EqualsIAscii(m_text)) { m_text = valueGiven; recompute = true; } - } - else{ + } else { Reset(); } - } - else - { + } else { Reset(); } if(recompute) { m_idSet.clear(); - if(m_ArgType == DbValueType::TextVal && m_text.size() > 0) - { - BeJsDocument doc; - doc.Parse(m_text.c_str()); - - if(FilterJSONStringIntoArray(doc) != BE_SQLITE_OK) - { - Reset(); - } - } - else + BeJsDocument doc; + doc.Parse(m_text.c_str()); + + if(FilterJSONStringIntoArray(doc) != BE_SQLITE_OK) { Reset(); + return BE_SQLITE_ERROR; } } m_index = m_idSet.begin(); diff --git a/iModelCore/ECDb/ECDb/BuiltInVTabs.h b/iModelCore/ECDb/ECDb/BuiltInVTabs.h index 41dc2947cb1..7bf9b7b26c8 100644 --- a/iModelCore/ECDb/ECDb/BuiltInVTabs.h +++ b/iModelCore/ECDb/ECDb/BuiltInVTabs.h @@ -57,7 +57,6 @@ struct IdSetModule : ECDbModule { Utf8String m_text; bset m_idSet; bset::iterator m_index; - DbValueType m_ArgType; public: IdSetCursor(IdSetTable& vt): ECDbCursor(vt){} diff --git a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp index 36805859030..3cfdde7a546 100644 --- a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp @@ -3,8 +3,6 @@ * See LICENSE.md in the repository root for full copyright notice. *--------------------------------------------------------------------------------------------*/ #include "ECDbPublishedTests.h" -#include "iostream" - USING_NAMESPACE_BENTLEY_EC BEGIN_ECDBUNITTESTS_NAMESPACE @@ -12,13 +10,16 @@ BEGIN_ECDBUNITTESTS_NAMESPACE //--------------------------------------------------------------------------------------- // @bsiclass //+---------------+---------------+---------------+---------------+---------------+------ -struct ECDbIdSetVirtualTableTests : ECDbTestFixture {}; +struct ECDbIdSetVirtualTableTestFixture : ECDbTestFixture {}; //--------------------------------------------------------------------------------------- // @bsimethod //+---------------+---------------+---------------+---------------+---------------+------ -TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { +TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetModuleTest) { ASSERT_EQ(BE_SQLITE_OK, SetupECDb("vtab.ecdb")); + m_ecdb.ExecuteSql("analyze"); + m_ecdb.SaveChanges(); + ReopenECDb(); { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM ECVLib.IdSet('[1,2,3,4,5]')"))); @@ -86,8 +87,8 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM ECVLib.IdSet('[0x1,0x2,\"3\",\"4\",\"5\"]')"))); - // Should fail while converting to json array because hex values with quotes are required so should be empty table - ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); + // Should fail while converting to json array because hex values with quotes are required so should be empty table and should throw error + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { ECSqlStatement stmt; @@ -104,8 +105,8 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM ECVLib.IdSet('[1,\"2\",3, 4.5, 5.6]')"))); - // Will not take into account 4.5 and 5.6 because they are decimal values so should be empty table - ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); + // Will not take into account 4.5 and 5.6 because they are decimal values so should be empty table and throw error + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { ECSqlStatement stmt; @@ -129,8 +130,8 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { IECSqlBinder& elementBinder = arrayBinder.AddArrayElement(); ASSERT_EQ(ECSqlStatus::Error, elementBinder.BindText( "[1,\"2\",3, \"abc\"]", IECSqlBinder::MakeCopy::No)); - // EmptyArray is Binded so should be empty table - ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); + // EmptyArray is Binded so should be empty table and should throw error because the ultimate json text which will be binded will be an empty json array and that is not allowed + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { ECSqlStatement stmt; @@ -180,8 +181,8 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { ASSERT_EQ(ECSqlStatus::Success, elementBinder.BindDouble(i)); } - // having null as an element so should be empty table - ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); + // having null as an element so should be empty table and should throw error + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { DPoint2d pArrayOfST1_P2D[] = {DPoint2d::From(-21, 22.1),DPoint2d::From(-85.34, 35.36),DPoint2d::From(-31.34, 12.35)}; @@ -200,8 +201,8 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { ASSERT_EQ(ECSqlStatus::Error, elementBinder.BindPoint3d(pArrayOfST1_P3D[i])); } - // EmptyArray is Binded so should be empty table - ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); + // EmptyArray is Binded so should be empty table and should throw error because the ultimate json text which will be binded will be an empty json array and that is not allowed + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { DPoint2d pArrayOfST1_P2D[] = {DPoint2d::From(-21, 22.1),DPoint2d::From(-85.34, 35.36),DPoint2d::From(-31.34, 12.35)}; @@ -223,8 +224,8 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { ASSERT_EQ(ECSqlStatus::Error, elementBinder["P3D"].BindPoint3d(pArrayOfST1_P3D[i])); } - // EmptyArray is Binded so should be empty table - ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); + // EmptyArray is Binded so should be empty table and should throw error because the ultimate json text which will be binded will be an empty json array and that is not allowed + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { const std::vector> bi_array = { @@ -238,8 +239,8 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { for(auto& m : bi_array) ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindBlob((void const*)&m[0], (int)m.size(), IECSqlBinder::MakeCopy::No)); - // Binary is Binded so should be empty table - ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); + // Binary is Binded so should be empty table and error should be thrown + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { const auto dt = DateTime(DateTime::Kind::Unspecified, 2017, 1, 17, 0, 0); @@ -253,8 +254,8 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindDateTime(dtUtc)); } - // EmptyArray is Binded so should be empty table - ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); + // EmptyArray is Binded so should be empty table and should throw error because the ultimate json text which will be binded will be an empty json array and that is not allowed + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { auto geom = IGeometry::Create(ICurvePrimitive::CreateLine(DSegment3d::From(0.0, 0.0, 0.0, 1.0, 1.0, 1.0))); @@ -266,8 +267,8 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindGeometry(*geom)); } - // Binary is Binded because BindGeometry internally calls so should be empty table - ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); + // Binary is Binded because BindGeometry internally calls so should be empty table and error should be thrown + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { ECSqlStatement stmt; @@ -278,8 +279,8 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindText("ABC",IECSqlBinder::MakeCopy::No)); } - // EmptyArray is Binded so should be empty table - ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); + // EmptyArray is Binded so should be empty table and should throw error because the ultimate json text which will be binded will be an empty json array and that is not allowed + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { ECSqlStatement stmt; @@ -290,8 +291,8 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindText("[abc]",IECSqlBinder::MakeCopy::No)); } - // EmptyArray is Binded so should be empty table - ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); + // EmptyArray is Binded so should be empty table and should throw error because the ultimate json text which will be binded will be an empty json array and that is not allowed + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { ECSqlStatement stmt; @@ -302,8 +303,8 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindText("[\"abc\"]",IECSqlBinder::MakeCopy::No)); } - // EmptyArray is Binded so should be empty table - ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); + // EmptyArray is Binded so should be empty table and should throw error because the ultimate json text which will be binded will be an empty json array and that is not allowed + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { ECSqlStatement stmt; @@ -328,7 +329,7 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT ECInstanceId FROM ECVLib.IdSet('[1,2,3,4,5]'), meta.ECClassDef where ECInstanceId = id group by ECInstanceId"))); - ASSERT_STREQ("7 0 0 SCAN IdSet VIRTUAL TABLE INDEX 1: (null) (null) (null) (null);12 0 0 SEARCH main.ec_Class USING INTEGER PRIMARY KEY (rowid=?) (null) (null) (null) (null);15 0 0 USE TEMP B-TREE FOR GROUP BY (null) (null) (null) (null)", m_ecdb.ExplainQuery(stmt.GetNativeSql(), true).c_str()); + ASSERT_STREQ("7 0 0 SCAN main.ec_Class (null) (null) (null) (null);9 0 0 SCAN IdSet VIRTUAL TABLE INDEX 1: (null) (null) (null) (null)", m_ecdb.ExplainQuery(stmt.GetNativeSql(), true).c_str()); } { ECSqlStatement stmt; @@ -373,6 +374,78 @@ TEST_F(ECDbIdSetVirtualTableTests, IdSetModuleTest) { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::InvalidECSql, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT ECClassId FROM ECVLib.IdSet(?,?), meta.ECClassDef where ECClassId = id group by ECClassId"))); } + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM ECVLib.IdSet('[1,1,1,1]')"))); + + int i = 0; + while (stmt.Step() == BE_SQLITE_ROW) + { + ASSERT_EQ(i+1, stmt.GetValueInt64(0)); + i++; + } + ASSERT_EQ(i, 1); + } + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM ECVLib.IdSet('[-1,-2,3,-4,5]')"))); + // negative valiues are not allowed so empty table and dhould throw error + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); + } + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM ECVLib.IdSet('[0,1,1,2]')"))); + // 0 is not allowed so empty table and dhould throw error + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); + } + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM ECVLib.IdSet('[\"-1\",\"-2\",\"3\",\"-4\",\"5\"]')"))); + // negative valiues are not allowed so empty table and dhould throw error + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); + } + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM ECVLib.IdSet('[\"0xFFFFFFFF\",3,4,5]')"))); + int i = 3; + while (i<=5) + { + ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()); + ASSERT_EQ(i, stmt.GetValueInt64(0)); + i++; + } + } + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM ECVLib.IdSet('[\"0x0\",3,4,5]')"))); + // negative valiues are not allowed so empty table and dhould throw error + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); + } + { + std::vector hexIds = std::vector{"0x1", "0x2", "0x3", "4", "5"}; + + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT ECInstanceId FROM ECVLib.IdSet(?), meta.ECClassDef where ECInstanceId = id group by ECInstanceId"))); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + for(int i =0;i Date: Mon, 7 Oct 2024 10:24:47 +0530 Subject: [PATCH 23/43] Performance test updated --- iModelCore/ECDb/Tests/Performance/PerformanceIdSetTests.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/iModelCore/ECDb/Tests/Performance/PerformanceIdSetTests.cpp b/iModelCore/ECDb/Tests/Performance/PerformanceIdSetTests.cpp index a6ac618f586..e73b3a33848 100644 --- a/iModelCore/ECDb/Tests/Performance/PerformanceIdSetTests.cpp +++ b/iModelCore/ECDb/Tests/Performance/PerformanceIdSetTests.cpp @@ -116,12 +116,12 @@ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet_with ASSERT_EQ(BentleyStatus::SUCCESS, SetupECDb("SimpleComparisonBetweenInVirtualSet_and_IdSet_with_custom_schema.ecdb", SchemaItem(R"xml( - + )xml"))); ECSqlStatement insert_stmt; - ASSERT_EQ(ECSqlStatus::Success, insert_stmt.Prepare(m_ecdb, "insert into ts.Foo(Ids) values(?)")); + ASSERT_EQ(ECSqlStatus::Success, insert_stmt.Prepare(m_ecdb, "insert into ts.Foo(UnIndexed_Prop) values(?)")); for(int i = 0;i<1000000;i++) { insert_stmt.ClearBindings(); @@ -131,7 +131,7 @@ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet_with ASSERT_EQ(BE_SQLITE_DONE, insert_stmt.Step()); } - std::vector v = {"ECInstanceId", "Ids"}; + std::vector v = {"ECInstanceId", "UnIndexed_Prop"}; for(auto& str: v) { ECSqlStatement stmt_With_InvirtualSet; From 7e7c0444ed8f5150586563df58ce443cb649f55c Mon Sep 17 00:00:00 2001 From: SohamBhattacharjee Date: Mon, 7 Oct 2024 11:14:04 +0530 Subject: [PATCH 24/43] Tests updated to prevent failure in pipeline --- iModelCore/ECDb/ECDb/BuiltInVTabs.h | 2 +- .../ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/iModelCore/ECDb/ECDb/BuiltInVTabs.h b/iModelCore/ECDb/ECDb/BuiltInVTabs.h index 7bf9b7b26c8..3917b2d9e86 100644 --- a/iModelCore/ECDb/ECDb/BuiltInVTabs.h +++ b/iModelCore/ECDb/ECDb/BuiltInVTabs.h @@ -81,7 +81,7 @@ struct IdSetModule : ECDbModule { IdSetModule(ECDbR db): ECDbModule( db, NAME, - "CREATE TABLE x(id, json_array_ids hidden)", + "CREATE TABLE x(id PRIMARY KEY, json_array_ids hidden)", R"xml( hexIds = std::vector{"0x1", "0x2", "0x3", "4", "5"}; From 5c159b4076fe77097aeb919130503dd281a68b92 Mon Sep 17 00:00:00 2001 From: SohamBhattacharjee Date: Mon, 7 Oct 2024 13:55:48 +0530 Subject: [PATCH 25/43] tests updated --- iModelCore/ECDb/ECDb/BuiltInVTabs.h | 2 +- .../ECDbIdSetVirtualTableTests.cpp | 99 ++++++++++++------- 2 files changed, 62 insertions(+), 39 deletions(-) diff --git a/iModelCore/ECDb/ECDb/BuiltInVTabs.h b/iModelCore/ECDb/ECDb/BuiltInVTabs.h index 3917b2d9e86..7bf9b7b26c8 100644 --- a/iModelCore/ECDb/ECDb/BuiltInVTabs.h +++ b/iModelCore/ECDb/ECDb/BuiltInVTabs.h @@ -81,7 +81,7 @@ struct IdSetModule : ECDbModule { IdSetModule(ECDbR db): ECDbModule( db, NAME, - "CREATE TABLE x(id PRIMARY KEY, json_array_ids hidden)", + "CREATE TABLE x(id, json_array_ids hidden)", R"xml( 1"))); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet('[1,2,3,4,5]') where id > 1")); int i = 1; while (stmt.Step() == BE_SQLITE_ROW) @@ -57,7 +57,7 @@ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetModuleTest) { std::vector hexIds = std::vector{"0x1", "0x2", "0x3", "4", "5"}; ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM ECVLib.IdSet(?)"))); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet(?)")); IECSqlBinder& arrayBinder = stmt.GetBinder(1); for(int i =0;i hexIds = std::vector{"0x1", "0x2", "0x3", "4", "5"}; ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT ECInstanceId FROM ECVLib.IdSet(?), meta.ECClassDef where ECInstanceId = id group by ECInstanceId"))); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT ECInstanceId FROM ECVLib.IdSet(?), meta.ECClassDef where ECInstanceId = id group by ECInstanceId")); IECSqlBinder& arrayBinder = stmt.GetBinder(1); for(int i =0;i ids = std::vector{0,1,1,2}; ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM ECVLib.IdSet('[0,1,1,2]')"))); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet(?)")); + IECSqlBinder& binder = stmt.GetBinder(1); + + for(auto& i : ids) + { + ASSERT_EQ(ECSqlStatus::Success, binder.AddArrayElement().BindInt(i)); + } // 0 is not allowed so empty table and dhould throw error ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM ECVLib.IdSet('[\"-1\",\"-2\",\"3\",\"-4\",\"5\"]')"))); - // negative valiues are not allowed so empty table and dhould throw error + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet('[\"-1\",\"-2\",\"3\",\"-4\",\"5\"]')")); + // negative values are not allowed so empty table and should throw error + ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); + } + { + std::vector stringIds = std::vector{"-1", "-2", "3", "-4", "5"}; + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet(?)")); + IECSqlBinder& binder = stmt.GetBinder(1); + + for(auto& i : stringIds) + { + if(i.EqualsIAscii("3") || i.EqualsIAscii("5")) + ASSERT_EQ(ECSqlStatus::Success, binder.AddArrayElement().BindText(i.c_str(), IECSqlBinder::MakeCopy::No)); + else + ASSERT_EQ(ECSqlStatus::Error, binder.AddArrayElement().BindText(i.c_str(), IECSqlBinder::MakeCopy::No)); + } + // Binding negative values will fail so for the negative values binder.AddArrayElement().BindText() will be empty array element which are not allowed so empty table and should throw error ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM ECVLib.IdSet('[\"0xFFFFFFFF\",3,4,5]')"))); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet('[\"0xFFFFFFFF\",3,4,5]')")); int i = 3; while (i<=5) { @@ -419,15 +442,15 @@ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetModuleTest) { } { ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT id FROM ECVLib.IdSet('[\"0x0\",3,4,5]')"))); - // negative valiues are not allowed so empty table and dhould throw error + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet('[\"0x0\",3,4,5]')")); + // 0 values are not allowed so empty table and should throw error ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { std::vector hexIds = std::vector{"0x1", "0x2", "0x3", "4", "5"}; ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, SqlPrintfString("SELECT ECInstanceId FROM ECVLib.IdSet(?), meta.ECClassDef where ECInstanceId = id group by ECInstanceId"))); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT ECInstanceId FROM ECVLib.IdSet(?), meta.ECClassDef where ECInstanceId = id group by ECInstanceId")); IECSqlBinder& arrayBinder = stmt.GetBinder(1); for(int i =0;i Date: Mon, 7 Oct 2024 19:29:19 +0530 Subject: [PATCH 26/43] update in logic in IModelJsNative.cpp and concurrentquery --- .../ECDb/ECDb/ConcurrentQueryManagerImpl.cpp | 10 +++++-- iModelJsNodeAddon/IModelJsNative.cpp | 29 ++++++++++++++++++- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/iModelCore/ECDb/ECDb/ConcurrentQueryManagerImpl.cpp b/iModelCore/ECDb/ECDb/ConcurrentQueryManagerImpl.cpp index 4b7a6724cef..0d2985756ad 100644 --- a/iModelCore/ECDb/ECDb/ConcurrentQueryManagerImpl.cpp +++ b/iModelCore/ECDb/ECDb/ConcurrentQueryManagerImpl.cpp @@ -1912,10 +1912,16 @@ bool ECSqlParams::TryBindTo(ECSqlStatement& stmt, std::string& err) const { IdSet set(param.GetValueIdSet()); for(auto& ids: set) { - st = ids.IsValid() ? binder.AddArrayElement().BindInt64((int64_t) ids.GetValue()) : binder.AddArrayElement().BindNull(); + if(!ids.IsValid()) + { + allElementsAdded = false; + break; + } + st = binder.AddArrayElement().BindInt64((int64_t) ids.GetValue()); if(!st.IsSuccess()) { allElementsAdded = false; + break; } } if(allElementsAdded) // If even one array element has failed to be added we set the status for the entire operation as ECSqlStatus::Error although for the time being we don't do anything with status even if it fails @@ -1924,9 +1930,7 @@ bool ECSqlParams::TryBindTo(ECSqlStatement& stmt, std::string& err) const { st = ECSqlStatus::Error; } else - { st = ECSqlStatus::Error; - } break; } diff --git a/iModelJsNodeAddon/IModelJsNative.cpp b/iModelJsNodeAddon/IModelJsNative.cpp index fe1cc242e3d..5d5c24399a9 100644 --- a/iModelJsNodeAddon/IModelJsNative.cpp +++ b/iModelJsNodeAddon/IModelJsNative.cpp @@ -3531,7 +3531,34 @@ struct NativeECSqlBinder : BeObjectWrap return Napi::Number::New(Env(), (int) BE_SQLITE_ERROR); idSet->insert(id); } - ECSqlStatus stat = m_binder->BindVirtualSet(idSet); + ECSqlStatus stat; + BinderInfo const& binderInfo = m_binder->GetBinderInfo(); + if(binderInfo.GetType() == BinderInfo::BinderType::VirtualSet) + stat = m_binder->BindVirtualSet(idSet); + else if(binderInfo.GetType() == BinderInfo::BinderType::Array && binderInfo.IsForIdSet()) + { + bool allElementsAdded = true; + for(auto it = idSet->begin(); it != idSet->end(); ++it) + { + if(!(*it).IsValid()) + { + allElementsAdded = false; + break; + } + stat = m_binder->AddArrayElement().BindInt64((int64_t) (*it).GetValue()); + if(!stat.IsSuccess()) + { + allElementsAdded = false; + break; + } + } + if(allElementsAdded) // If even one array element has failed to be added we set the status for the entire operation as ECSqlStatus::Error + stat = ECSqlStatus::Success; + else + stat = ECSqlStatus::Error; + } + else + stat = ECSqlStatus::Error; return Napi::Number::New(Env(), (int) ToDbResult(stat)); } From 9d4d08ddf2a835aac6ceae7709c5d1bea6cda9ed Mon Sep 17 00:00:00 2001 From: SohamBhattacharjee Date: Tue, 8 Oct 2024 00:56:35 +0530 Subject: [PATCH 27/43] performance tests indentation updated --- iModelCore/ECDb/Tests/Performance/PerformanceIdSetTests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iModelCore/ECDb/Tests/Performance/PerformanceIdSetTests.cpp b/iModelCore/ECDb/Tests/Performance/PerformanceIdSetTests.cpp index e73b3a33848..e9227e3fe55 100644 --- a/iModelCore/ECDb/Tests/Performance/PerformanceIdSetTests.cpp +++ b/iModelCore/ECDb/Tests/Performance/PerformanceIdSetTests.cpp @@ -108,7 +108,7 @@ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet_with LOGTODB(TEST_DETAILS, timer_IdSet.GetElapsedSeconds(), i, SqlPrintfString("Statement: \"%s\"", stmt_With_IdSet.GetECSql())); } - //--------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------- // @bsiclass //+---------------+---------------+---------------+---------------+---------------+------ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet_with_custom_schema) From 3cc80e6499bcd2e9b5d2ffae4dc0e9de3fcefefd Mon Sep 17 00:00:00 2001 From: SohamBhattacharjee Date: Tue, 8 Oct 2024 13:28:42 +0530 Subject: [PATCH 28/43] final update --- iModelCore/ECDb/ECDb/BuiltInVTabs.cpp | 11 ++++- iModelCore/ECDb/ECDb/BuiltInVTabs.h | 4 +- .../ECDb/ECSql/ECSqlPreparedStatement.cpp | 2 +- .../ECDb/ECDb/ECSql/ECSqlPreparedStatement.h | 3 +- .../ECSql/PragmaECSqlPreparedStatement.cpp | 1 - .../ECDb/ECSql/PragmaECSqlPreparedStatement.h | 1 - .../ECDbIdSetVirtualTableTests.cpp | 43 ++++++++++++++++--- 7 files changed, 52 insertions(+), 13 deletions(-) diff --git a/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp b/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp index a5c65b09868..43145ed9861 100644 --- a/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp +++ b/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp @@ -186,11 +186,17 @@ DbResult IdSetModule::IdSetTable::IdSetCursor::GetColumn(int i, Context& ctx) { //--------------------------------------------------------------------------------------- DbResult IdSetModule::IdSetTable::IdSetCursor::FilterJSONStringIntoArray(BeJsDocument& doc) { if(!doc.isArray()) + { + GetTable().SetError("IdSet vtab: The argument should be a valid JSON array of ids"); return BE_SQLITE_ERROR; - bool flag = doc.ForEachArrayMember([&](BeJsValue::ArrayIndex, BeJsConst k1) + } + bool flag = doc.ForEachArrayMember([&](BeJsValue::ArrayIndex a, BeJsConst k1) { if(BE_SQLITE_OK != FilterJSONBasedOnType(k1)) + { + GetTable().SetError(SqlPrintfString("IdSet vtab: The element with index %u is invalid", a)); return true; + } return false; }); if(flag) @@ -256,6 +262,7 @@ DbResult IdSetModule::IdSetTable::IdSetCursor::Filter(int idxNum, const char *id if(FilterJSONStringIntoArray(doc) != BE_SQLITE_OK) { Reset(); + m_index = m_idSet.begin(); return BE_SQLITE_ERROR; } } @@ -267,7 +274,7 @@ DbResult IdSetModule::IdSetTable::IdSetCursor::Filter(int idxNum, const char *id // @bsimethod //--------------------------------------------------------------------------------------- void IdSetModule::IdSetTable::IdSetCursor::Reset() { - m_text = ""; + m_text = "[]"; m_idSet.clear(); } //--------------------------------------------------------------------------------------- diff --git a/iModelCore/ECDb/ECDb/BuiltInVTabs.h b/iModelCore/ECDb/ECDb/BuiltInVTabs.h index 7bf9b7b26c8..279db540286 100644 --- a/iModelCore/ECDb/ECDb/BuiltInVTabs.h +++ b/iModelCore/ECDb/ECDb/BuiltInVTabs.h @@ -56,10 +56,10 @@ struct IdSetModule : ECDbModule { private: Utf8String m_text; bset m_idSet; - bset::iterator m_index; + bset::const_iterator m_index; public: - IdSetCursor(IdSetTable& vt): ECDbCursor(vt){} + IdSetCursor(IdSetTable& vt): ECDbCursor(vt), m_index(m_idSet.begin()){} bool Eof() final { return m_index == m_idSet.end() ; } DbResult Next() final; DbResult GetColumn(int i, Context& ctx) final; diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp index 20791c48807..58d6802fd13 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp @@ -68,6 +68,7 @@ int IECSqlPreparedStatement::TryGetParameterIndex(Utf8CP parameterName) const //--------------------------------------------------------------------------------------- ECSqlStatus IECSqlPreparedStatement::Reset() { + m_onBeforeFirstStepNotCalled = true; if (SUCCESS != AssertIsValid()) return ECSqlStatus::Error; @@ -269,7 +270,6 @@ ECSqlStatus SingleECSqlPreparedStatement::_Reset() if (nativeSqlStat != BE_SQLITE_OK) return ECSqlStatus(nativeSqlStat); - m_onBeforeFirstStepNotCalled = true; return ECSqlStatus::Success; } diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h index 0179883f7f3..4c5e07f3453 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h @@ -24,6 +24,8 @@ struct IECSqlPreparedStatement ECSqlType m_type; bool m_isCompoundStatement; bool m_isNoopInSqlite = false; + bool m_onBeforeFirstStepNotCalled = true; + private: Utf8String m_ecsql; @@ -80,7 +82,6 @@ struct SingleECSqlPreparedStatement : IECSqlPreparedStatement private: mutable BeSQLite::Statement m_sqliteStatement; ECSqlParameterMap m_parameterMap; - bool m_onBeforeFirstStepNotCalled = true; IECSqlBinder& _GetBinder(int parameterIndex) const override; int _GetParameterIndex(Utf8CP parameterName) const override; diff --git a/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.cpp b/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.cpp index 32a75e76e89..10facf7c295 100644 --- a/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.cpp @@ -605,7 +605,6 @@ ECSqlStatus PragmaECSqlPreparedStatement::_Reset() { if (rc != BE_SQLITE_OK) return ECSqlStatus(rc); - m_onBeforeFirstStepNotCalled = true; return ECSqlStatus::Success; } //--------------------------------------------------------------------------------------- diff --git a/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.h b/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.h index 91cac82aa50..68d3ac3340c 100644 --- a/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.h +++ b/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.h @@ -205,7 +205,6 @@ struct PragmaECSqlPreparedStatement final: public IECSqlPreparedStatement { private: ECSqlParameterMap m_parameterMap; std::unique_ptr m_resultSet; - bool m_onBeforeFirstStepNotCalled = true; IECSqlBinder& _GetBinder(int parameterIndex) const override; int _GetParameterIndex(Utf8CP parameterName) const override; int _TryGetParameterIndex(Utf8CP parameterName) const override; diff --git a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp index 19367724ea7..80d7f3cce3c 100644 --- a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp @@ -17,9 +17,6 @@ struct ECDbIdSetVirtualTableTestFixture : ECDbTestFixture {}; //+---------------+---------------+---------------+---------------+---------------+------ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetModuleTest) { ASSERT_EQ(BE_SQLITE_OK, SetupECDb("vtab.ecdb")); - m_ecdb.ExecuteSql("analyze"); - m_ecdb.SaveChanges(); - ReopenECDb(); { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet('[1,2,3,4,5]')")); @@ -329,8 +326,8 @@ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetModuleTest) { { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT ECInstanceId FROM ECVLib.IdSet('[1,2,3,4,5]'), meta.ECClassDef where ECInstanceId = id group by ECInstanceId")); - ASSERT_EQ(true, m_ecdb.ExplainQuery(stmt.GetNativeSql(), true).Contains("SCAN main.ec_Class")); ASSERT_EQ(true, m_ecdb.ExplainQuery(stmt.GetNativeSql(), true).Contains("SCAN IdSet VIRTUAL TABLE INDEX 1")); + ASSERT_EQ(true, m_ecdb.ExplainQuery(stmt.GetNativeSql(), true).Contains("SEARCH main.ec_Class USING INTEGER PRIMARY KEY (rowid=?)")); } { ECSqlStatement stmt; @@ -458,7 +455,7 @@ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetModuleTest) { ASSERT_EQ(ECSqlStatus::Success, elementBinder.BindText(hexIds[i].c_str(), IECSqlBinder::MakeCopy::No)); } int i = 0; - while (i<=1) + while (i<=2) { ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()); ASSERT_EQ(BeStringUtilities::ParseHex(hexIds[i++].c_str()), stmt.GetValueInt64(0)); @@ -469,8 +466,44 @@ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetModuleTest) { ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()); ASSERT_EQ(BeStringUtilities::ParseHex(hexIds[i++].c_str()), stmt.GetValueInt64(0)); + ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()); + ASSERT_EQ(BeStringUtilities::ParseHex(hexIds[i++].c_str()), stmt.GetValueInt64(0)); + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()); } + { + std::vector ids = std::vector{"0x1", "0x2", "0x3", "4.5", "5.5"}; + + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT ECInstanceId FROM ECVLib.IdSet(?), meta.ECClassDef where ECInstanceId = id group by ECInstanceId")); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + for(int i =0;i dec_ids = std::vector{1, 2, 4.5, 3, 5.5}; + IECSqlBinder& arrayBinder_two = stmt.GetBinder(1); + for(int i =0;i Date: Tue, 8 Oct 2024 17:47:51 +0530 Subject: [PATCH 29/43] OnBeforeFirstStep() logic updated by using m_isFirstStep flag --- iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp | 9 ++++----- iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h | 2 +- .../ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.cpp | 10 ++++++---- .../ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.h | 1 + 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp index 58d6802fd13..759571dd6ab 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp @@ -68,7 +68,6 @@ int IECSqlPreparedStatement::TryGetParameterIndex(Utf8CP parameterName) const //--------------------------------------------------------------------------------------- ECSqlStatus IECSqlPreparedStatement::Reset() { - m_onBeforeFirstStepNotCalled = true; if (SUCCESS != AssertIsValid()) return ECSqlStatus::Error; @@ -212,20 +211,19 @@ DbResult SingleECSqlPreparedStatement::DoStep() if (SUCCESS != AssertIsValid()) return BE_SQLITE_ERROR; - if(m_onBeforeFirstStepNotCalled) + if(m_isFirstStep) { if (!m_parameterMap.OnBeforeFirstStep().IsSuccess()) return BE_SQLITE_ERROR; - m_onBeforeFirstStepNotCalled = false; } const DbResult nativeSqlStatus = m_sqliteStatement.Step(); - switch (nativeSqlStatus) { case BE_SQLITE_ROW: case BE_SQLITE_DONE: + m_isFirstStep = false; // if step actually successded on the sqlite side then we set this flag to false break; case BE_SQLITE_INTERRUPT: @@ -240,7 +238,7 @@ DbResult SingleECSqlPreparedStatement::DoStep() break; } } - + return nativeSqlStatus; } @@ -270,6 +268,7 @@ ECSqlStatus SingleECSqlPreparedStatement::_Reset() if (nativeSqlStat != BE_SQLITE_OK) return ECSqlStatus(nativeSqlStat); + m_isFirstStep = true; // When everything is reset succesffully we reset this flag return ECSqlStatus::Success; } diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h index 4c5e07f3453..d44f89f7d6b 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h @@ -24,7 +24,6 @@ struct IECSqlPreparedStatement ECSqlType m_type; bool m_isCompoundStatement; bool m_isNoopInSqlite = false; - bool m_onBeforeFirstStepNotCalled = true; private: @@ -82,6 +81,7 @@ struct SingleECSqlPreparedStatement : IECSqlPreparedStatement private: mutable BeSQLite::Statement m_sqliteStatement; ECSqlParameterMap m_parameterMap; + bool m_isFirstStep = true; IECSqlBinder& _GetBinder(int parameterIndex) const override; int _GetParameterIndex(Utf8CP parameterName) const override; diff --git a/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.cpp b/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.cpp index 10facf7c295..ffb62b24760 100644 --- a/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.cpp @@ -605,6 +605,7 @@ ECSqlStatus PragmaECSqlPreparedStatement::_Reset() { if (rc != BE_SQLITE_OK) return ECSqlStatus(rc); + m_isFirstStep = true; // Will reset the flag when actually everything will be reset successfully return ECSqlStatus::Success; } //--------------------------------------------------------------------------------------- @@ -634,15 +635,16 @@ DbResult PragmaECSqlPreparedStatement::DoStep() { if (SUCCESS != AssertIsValid()) return BE_SQLITE_ERROR; - if(m_onBeforeFirstStepNotCalled) + if(m_isFirstStep) { if (!m_parameterMap.OnBeforeFirstStep().IsSuccess()) return BE_SQLITE_ERROR; - m_onBeforeFirstStepNotCalled = false; } - - return m_resultSet->Step(); + DbResult res = m_resultSet->Step(); + if(res == BE_SQLITE_DONE || res == BE_SQLITE_ROW) + m_isFirstStep = false; // if step actually successded on the sqlite side then we set this flag to false + return res; } //--------------------------------------------------------------------------------------- diff --git a/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.h b/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.h index 68d3ac3340c..575bcc03215 100644 --- a/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.h +++ b/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.h @@ -205,6 +205,7 @@ struct PragmaECSqlPreparedStatement final: public IECSqlPreparedStatement { private: ECSqlParameterMap m_parameterMap; std::unique_ptr m_resultSet; + bool m_isFirstStep = true; IECSqlBinder& _GetBinder(int parameterIndex) const override; int _GetParameterIndex(Utf8CP parameterName) const override; int _TryGetParameterIndex(Utf8CP parameterName) const override; From 4b33a80b5a51b9aec2517e0e59c1d08ab77183d4 Mon Sep 17 00:00:00 2001 From: SohamBhattacharjee Date: Thu, 17 Oct 2024 13:40:42 +0530 Subject: [PATCH 30/43] More Tests added --- .../ECDbIdSetVirtualTableTests.cpp | 148 +++++++++++++++--- 1 file changed, 130 insertions(+), 18 deletions(-) diff --git a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp index 80d7f3cce3c..b416e4496ae 100644 --- a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp @@ -84,7 +84,7 @@ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetModuleTest) { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet('[0x1,0x2,\"3\",\"4\",\"5\"]')")); - // Should fail while converting to json array because hex values with quotes are required so should be empty table and should throw error + // Should fail while converting to json array because hex values with quotes are required so should be empty table and should log error ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { @@ -102,7 +102,7 @@ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetModuleTest) { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet('[1,\"2\",3, 4.5, 5.6]')")); - // Will not take into account 4.5 and 5.6 because they are decimal values so should be empty table and throw error + // Will not take into account 4.5 and 5.6 because they are decimal values so should be empty table and log error ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { @@ -127,7 +127,7 @@ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetModuleTest) { IECSqlBinder& elementBinder = arrayBinder.AddArrayElement(); ASSERT_EQ(ECSqlStatus::Error, elementBinder.BindText( "[1,\"2\",3, \"abc\"]", IECSqlBinder::MakeCopy::No)); - // EmptyArray is Binded so should be empty table and should throw error because the ultimate json text which will be binded will be an empty json array and that is not allowed + // EmptyArray is Binded so should be empty table and should log error because the ultimate json text which will be binded will be an empty json array and that is not allowed ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { @@ -178,7 +178,7 @@ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetModuleTest) { ASSERT_EQ(ECSqlStatus::Success, elementBinder.BindDouble(i)); } - // having null as an element so should be empty table and should throw error + // having null as an element so should be empty table and should log error ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { @@ -198,7 +198,7 @@ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetModuleTest) { ASSERT_EQ(ECSqlStatus::Error, elementBinder.BindPoint3d(pArrayOfST1_P3D[i])); } - // EmptyArray is Binded so should be empty table and should throw error because the ultimate json text which will be binded will be an empty json array and that is not allowed + // EmptyArray is Binded so should be empty table and should log error because the ultimate json text which will be binded will be an empty json array and that is not allowed ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { @@ -221,7 +221,7 @@ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetModuleTest) { ASSERT_EQ(ECSqlStatus::Error, elementBinder["P3D"].BindPoint3d(pArrayOfST1_P3D[i])); } - // EmptyArray is Binded so should be empty table and should throw error because the ultimate json text which will be binded will be an empty json array and that is not allowed + // EmptyArray is Binded so should be empty table and should log error because the ultimate json text which will be binded will be an empty json array and that is not allowed ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { @@ -251,7 +251,7 @@ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetModuleTest) { ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindDateTime(dtUtc)); } - // EmptyArray is Binded so should be empty table and should throw error because the ultimate json text which will be binded will be an empty json array and that is not allowed + // EmptyArray is Binded so should be empty table and should log error because the ultimate json text which will be binded will be an empty json array and that is not allowed ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { @@ -276,7 +276,7 @@ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetModuleTest) { ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindText("ABC",IECSqlBinder::MakeCopy::No)); } - // EmptyArray is Binded so should be empty table and should throw error because the ultimate json text which will be binded will be an empty json array and that is not allowed + // EmptyArray is Binded so should be empty table and should log error because the ultimate json text which will be binded will be an empty json array and that is not allowed ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { @@ -288,7 +288,7 @@ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetModuleTest) { ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindText("[abc]",IECSqlBinder::MakeCopy::No)); } - // EmptyArray is Binded so should be empty table and should throw error because the ultimate json text which will be binded will be an empty json array and that is not allowed + // EmptyArray is Binded so should be empty table and should log error because the ultimate json text which will be binded will be an empty json array and that is not allowed ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { @@ -300,7 +300,7 @@ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetModuleTest) { ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindText("[\"abc\"]",IECSqlBinder::MakeCopy::No)); } - // EmptyArray is Binded so should be empty table and should throw error because the ultimate json text which will be binded will be an empty json array and that is not allowed + // EmptyArray is Binded so should be empty table and should log error because the ultimate json text which will be binded will be an empty json array and that is not allowed ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { @@ -388,7 +388,7 @@ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetModuleTest) { { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet('[-1,-2,3,-4,5]')")); - // negative values are not allowed so empty table and should throw error + // negative values are not allowed so empty table and should log error ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { @@ -401,13 +401,13 @@ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetModuleTest) { { ASSERT_EQ(ECSqlStatus::Success, binder.AddArrayElement().BindInt(i)); } - // 0 is not allowed so empty table and dhould throw error + // 0 is not allowed so empty table and dhould log error ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet('[\"-1\",\"-2\",\"3\",\"-4\",\"5\"]')")); - // negative values are not allowed so empty table and should throw error + // negative values are not allowed so empty table and should log error ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { @@ -423,7 +423,7 @@ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetModuleTest) { else ASSERT_EQ(ECSqlStatus::Error, binder.AddArrayElement().BindText(i.c_str(), IECSqlBinder::MakeCopy::No)); } - // Binding negative values will fail so for the negative values binder.AddArrayElement().BindText() will be empty array element which are not allowed so empty table and should throw error + // Binding negative values will fail so for the negative values binder.AddArrayElement().BindText() will be empty array element which are not allowed so empty table and should log error ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { @@ -440,7 +440,7 @@ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetModuleTest) { { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet('[\"0x0\",3,4,5]')")); - // 0 values are not allowed so empty table and should throw error + // 0 values are not allowed so empty table and should log error ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } { @@ -486,11 +486,11 @@ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetModuleTest) { ASSERT_EQ(ECSqlStatus::Success, elementBinder.BindText(ids[i].c_str(), IECSqlBinder::MakeCopy::No)); } - // "4.5" and "5.5" are not allowed to bind so empty array element so should fail and throw error + // "4.5" and "5.5" are not allowed to bind so empty array element so should fail and log error ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); ECSqlStatus stat = stmt.Reset(); - ASSERT_EQ(stat.IsSQLiteError(), true ); // Maybe as we are not stepping successfully in the statement so reset fails from sqlite side + ASSERT_EQ(stat.IsSQLiteError(), true ); // As we are not stepping successfully in the statement so reset fails from sqlite side ASSERT_EQ(ECSqlStatus::Success, stmt.ClearBindings()); std::vector dec_ids = std::vector{1, 2, 4.5, 3, 5.5}; @@ -501,9 +501,121 @@ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetModuleTest) { ASSERT_EQ(ECSqlStatus::Success, elementBinder.BindDouble(dec_ids[i])); } - // 4.5 and 5.5 are not allowed so should fail and throw error + // 4.5 and 5.5 are not allowed so should fail and log error ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } + { + std::vector ids = std::vector{"0x1", "0x2", "0x3", "4.5", "5.5"}; + + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT ECInstanceId FROM ECVLib.IdSet(?), meta.ECClassDef where ECInstanceId = id group by ECInstanceId")); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + for(int i =0;i dec_ids = std::vector{1, 2, 4.0, 3, 5.0}; + IECSqlBinder& arrayBinder_two = stmt.GetBinder(1); + for(int i =0;i dec_ids = std::vector{1, 2, 4.0, 3, 5.0}; + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + for(int i =0;i dec_ids = std::vector{0,1,2,3.0}; + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + for(int i =0;i dec_ids = std::vector{"0","1","2","3.0"}; + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + for(int i =0;i Date: Mon, 21 Oct 2024 11:27:05 +0530 Subject: [PATCH 31/43] Added flag checking to m_isFirstStep flag so that when actually flag is false we reset it to true and vice versa --- iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp | 6 ++++-- .../ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.cpp | 7 ++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp index 759571dd6ab..7f87df027fe 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp @@ -223,7 +223,8 @@ DbResult SingleECSqlPreparedStatement::DoStep() { case BE_SQLITE_ROW: case BE_SQLITE_DONE: - m_isFirstStep = false; // if step actually successded on the sqlite side then we set this flag to false + if(m_isFirstStep) + m_isFirstStep = false; // if step actually successded on the sqlite side then we set this flag to false if flag is true break; case BE_SQLITE_INTERRUPT: @@ -268,7 +269,8 @@ ECSqlStatus SingleECSqlPreparedStatement::_Reset() if (nativeSqlStat != BE_SQLITE_OK) return ECSqlStatus(nativeSqlStat); - m_isFirstStep = true; // When everything is reset succesffully we reset this flag + if(!m_isFirstStep) + m_isFirstStep = true; // When everything is reset successfully we reset this flag if flag is false return ECSqlStatus::Success; } diff --git a/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.cpp b/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.cpp index ffb62b24760..d865ae72295 100644 --- a/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.cpp @@ -605,7 +605,8 @@ ECSqlStatus PragmaECSqlPreparedStatement::_Reset() { if (rc != BE_SQLITE_OK) return ECSqlStatus(rc); - m_isFirstStep = true; // Will reset the flag when actually everything will be reset successfully + if(!m_isFirstStep) + m_isFirstStep = true; // Will reset the flag when actually everything will be reset successfully if flag is false return ECSqlStatus::Success; } //--------------------------------------------------------------------------------------- @@ -642,8 +643,8 @@ DbResult PragmaECSqlPreparedStatement::DoStep() { } DbResult res = m_resultSet->Step(); - if(res == BE_SQLITE_DONE || res == BE_SQLITE_ROW) - m_isFirstStep = false; // if step actually successded on the sqlite side then we set this flag to false + if((res == BE_SQLITE_DONE || res == BE_SQLITE_ROW) && m_isFirstStep) + m_isFirstStep = false; // if step actually successded on the sqlite side then we set this flag to false if flag is true return res; } From 8d1a2ccdd0166ddab319988dfcd6c630d52e7d4b Mon Sep 17 00:00:00 2001 From: soham-bentley <177123878+soham-bentley@users.noreply.github.com> Date: Thu, 24 Oct 2024 19:19:00 +0530 Subject: [PATCH 32/43] removing m_isFirstStep and identifying first step using statement state --- iModelCore/BeSQLite/BeSQLite.cpp | 11 +++++++++++ .../BeSQLite/PublicAPI/BeSQLite/BeSQLite.h | 16 ++++++++++++++++ iModelCore/BeSQLite/SQLite/bentley-sqlite.c | 9 +++++++++ .../ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp | 6 +----- .../ECDb/ECDb/ECSql/ECSqlPreparedStatement.h | 1 - 5 files changed, 37 insertions(+), 6 deletions(-) diff --git a/iModelCore/BeSQLite/BeSQLite.cpp b/iModelCore/BeSQLite/BeSQLite.cpp index 437f56111c1..95ca9c15da0 100644 --- a/iModelCore/BeSQLite/BeSQLite.cpp +++ b/iModelCore/BeSQLite/BeSQLite.cpp @@ -42,6 +42,7 @@ USING_NAMESPACE_BENTLEY_SQLITE extern "C" int checkNoActiveStatements(SqlDbP db); #endif +extern "C" int getStatementState(SqlStatementP pStmt); extern "C" int sqlite3_shathree_init(sqlite3 *, char **, const sqlite3_api_routines *); BEGIN_BENTLEY_SQLITE_NAMESPACE @@ -919,6 +920,16 @@ void Statement::DumpResults() Reset(); } + +/*--------------------------------------------------------------------------------------- +* @bsimethod ++---------------+---------------+---------------+---------------+---------------+------*/ +StatementState Statement::GetStatementState() + { + if(!IsPrepared()) + return StatementState::NOT_PREPARED; + return (StatementState)getStatementState(m_stmt); + } /*---------------------------------------------------------------------------------**//** * @bsimethod diff --git a/iModelCore/BeSQLite/PublicAPI/BeSQLite/BeSQLite.h b/iModelCore/BeSQLite/PublicAPI/BeSQLite/BeSQLite.h index cf2fc429b8c..00dde5d7ab9 100644 --- a/iModelCore/BeSQLite/PublicAPI/BeSQLite/BeSQLite.h +++ b/iModelCore/BeSQLite/PublicAPI/BeSQLite/BeSQLite.h @@ -434,6 +434,19 @@ enum class DbDeserializeOptions { }; ENUM_IS_FLAGS(DbDeserializeOptions) +//======================================================================================= +// @bsiclass +//======================================================================================= +enum StatementState +{ + // The first four values should be in accordance with sqlite3.c #define values with the same names + VDBE_INIT_STATE = 0, //!< Prepared statement under construction + VDBE_READY_STATE = 1, //!< Ready to run but not yet started + VDBE_RUN_STATE = 2, //!< Run in progress + VDBE_HALT_STATE = 3, //!< Finished. Need reset() or finalize() + NOT_PREPARED = 4, //!< Statement not yet prepared +}; + //======================================================================================= // @bsiclass //======================================================================================= @@ -985,6 +998,9 @@ struct Statement : NonCopyableClass //! Dump query results to stdout, for debugging purposes BE_SQLITE_EXPORT void DumpResults(); + //! Gets the state in which a particular statement is + BE_SQLITE_EXPORT StatementState GetStatementState(); + SqlStatementP GetSqlStatementP() const {return m_stmt;} // for direct use of sqlite3 api operator SqlStatementP(){return m_stmt;} // for direct use of sqlite3 api }; diff --git a/iModelCore/BeSQLite/SQLite/bentley-sqlite.c b/iModelCore/BeSQLite/SQLite/bentley-sqlite.c index ec7ced9183d..35d454244ca 100644 --- a/iModelCore/BeSQLite/SQLite/bentley-sqlite.c +++ b/iModelCore/BeSQLite/SQLite/bentley-sqlite.c @@ -99,3 +99,12 @@ int checkNoActiveStatements(sqlite3* db) return SQLITE_ERROR; } #endif + +/*---------------------------------------------------------------------------------**//** +* @bsimethod ++---------------+---------------+---------------+---------------+---------------+------*/ +int getStatementState(sqlite3_stmt *pStmt) + { + Vdbe* stmt = (Vdbe*)pStmt; + return stmt->eVdbeState; + } diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp index 7f87df027fe..73c41066b02 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp @@ -211,7 +211,7 @@ DbResult SingleECSqlPreparedStatement::DoStep() if (SUCCESS != AssertIsValid()) return BE_SQLITE_ERROR; - if(m_isFirstStep) + if(m_sqliteStatement.GetStatementState() == StatementState::VDBE_READY_STATE) { if (!m_parameterMap.OnBeforeFirstStep().IsSuccess()) return BE_SQLITE_ERROR; @@ -223,8 +223,6 @@ DbResult SingleECSqlPreparedStatement::DoStep() { case BE_SQLITE_ROW: case BE_SQLITE_DONE: - if(m_isFirstStep) - m_isFirstStep = false; // if step actually successded on the sqlite side then we set this flag to false if flag is true break; case BE_SQLITE_INTERRUPT: @@ -269,8 +267,6 @@ ECSqlStatus SingleECSqlPreparedStatement::_Reset() if (nativeSqlStat != BE_SQLITE_OK) return ECSqlStatus(nativeSqlStat); - if(!m_isFirstStep) - m_isFirstStep = true; // When everything is reset successfully we reset this flag if flag is false return ECSqlStatus::Success; } diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h index d44f89f7d6b..d96a5b0ebdf 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.h @@ -81,7 +81,6 @@ struct SingleECSqlPreparedStatement : IECSqlPreparedStatement private: mutable BeSQLite::Statement m_sqliteStatement; ECSqlParameterMap m_parameterMap; - bool m_isFirstStep = true; IECSqlBinder& _GetBinder(int parameterIndex) const override; int _GetParameterIndex(Utf8CP parameterName) const override; From cde738efccec31a39255f6429e2b226226294816 Mon Sep 17 00:00:00 2001 From: soham-bentley <177123878+soham-bentley@users.noreply.github.com> Date: Thu, 16 Jan 2025 14:12:40 +0530 Subject: [PATCH 33/43] Comment updated --- iModelCore/BeSQLite/PublicAPI/BeSQLite/BeSQLite.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iModelCore/BeSQLite/PublicAPI/BeSQLite/BeSQLite.h b/iModelCore/BeSQLite/PublicAPI/BeSQLite/BeSQLite.h index 17a0dfebb93..489339144a1 100644 --- a/iModelCore/BeSQLite/PublicAPI/BeSQLite/BeSQLite.h +++ b/iModelCore/BeSQLite/PublicAPI/BeSQLite/BeSQLite.h @@ -439,7 +439,7 @@ ENUM_IS_FLAGS(DbDeserializeOptions) //======================================================================================= enum StatementState { - // The first four values should be in accordance with sqlite3.c #define values with the same names + // The first four values are in accordance with sqlite3.c VDBE_INIT_STATE = 0, //!< Prepared statement under construction VDBE_READY_STATE = 1, //!< Ready to run but not yet started VDBE_RUN_STATE = 2, //!< Run in progress From a7aa217991ce9e26e74678f98461bd9c8b6533eb Mon Sep 17 00:00:00 2001 From: soham-bentley <177123878+soham-bentley@users.noreply.github.com> Date: Sat, 18 Jan 2025 02:53:20 +0530 Subject: [PATCH 34/43] Fixed the issue with the query SELECT e.i FROM aps.TestElement e INNER JOIN ECVLib.IdSet(?) v ON e.ECInstanceId = v.id --- iModelCore/ECDb/ECDb/ECSql/ECSqlPreparer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparer.cpp b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparer.cpp index 77fa82fb130..c8e60af4b50 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparer.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparer.cpp @@ -684,7 +684,7 @@ void ECSqlExpPreparer::RemovePropertyRefs(ECSqlPrepareContext& ctx, ClassRefExp if (propertyNameExp->IsPropertyRef()) continue; if (propertyNameExp->IsVirtualProperty()) - break; + continue; const RangeClassRefExp* classRefExp = propertyNameExp->GetClassRefExp(); if (&exp == classRefExp) From e09dd7c9088b1a7f65c8cb71942e35d7624b3ed1 Mon Sep 17 00:00:00 2001 From: soham-bentley <177123878+soham-bentley@users.noreply.github.com> Date: Mon, 20 Jan 2025 16:51:29 +0530 Subject: [PATCH 35/43] More tests added --- .../ECDbIdSetVirtualTableTests.cpp | 187 ++++++++++++++++++ 1 file changed, 187 insertions(+) diff --git a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp index b416e4496ae..a4939ded263 100644 --- a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp @@ -618,5 +618,192 @@ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetModuleTest) { } } +//--------------------------------------------------------------------------------------- +// @bsimethod +//+---------------+---------------+---------------+---------------+---------------+------ +TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetWithJOINS) { + ASSERT_EQ(BentleyStatus::SUCCESS, SetupECDb("IdSet_with_JOINS.ecdb", SchemaItem(R"xml( + + + + + + )xml"))); + std::vector listOfIds; + std::vector listOfStringVal = {"str1", "str2", "str3", "str4","str5", "str6", "str7", "str8", "str9", "str10"}; + ECSqlStatement insertStmt; + ASSERT_EQ(ECSqlStatus::Success, insertStmt.Prepare(m_ecdb, "INSERT INTO ts.A(str_prop, int_prop) VALUES(?,?)")); + for(int i =1;i<=10;i++) + { + insertStmt.BindText(1, listOfStringVal[i-1], IECSqlBinder::MakeCopy::No); + insertStmt.BindInt(2, i); + ECInstanceKey key; + if(insertStmt.Step(key) != BE_SQLITE_DONE) + break; + listOfIds.push_back((BeInt64Id)key.GetInstanceId()); + insertStmt.ClearBindings(); + insertStmt.Reset(); + } + ASSERT_EQ(10, listOfIds.size()); + if("testing normal joins with IdSet") + { + ECSqlStatement selectStmt; + ASSERT_EQ(ECSqlStatus::Success, selectStmt.Prepare(m_ecdb, "Select test.str_prop, test.int_prop, v.id from ts.A test JOIN ECVLib.IdSet(?) v on test.ECInstanceId = v.id")); + IECSqlBinder& binder = selectStmt.GetBinder(1); + ASSERT_EQ(ECSqlStatus::Success,binder.AddArrayElement().BindId(listOfIds[3])); + ASSERT_EQ(ECSqlStatus::Success,binder.AddArrayElement().BindId(listOfIds[6])); + ASSERT_EQ(ECSqlStatus::Success,binder.AddArrayElement().BindId(listOfIds[7])); + ASSERT_STREQ("str_prop", selectStmt.GetColumnInfo(0).GetProperty()->GetName().c_str()); + ASSERT_STREQ("int_prop", selectStmt.GetColumnInfo(1).GetProperty()->GetName().c_str()); + ASSERT_STREQ("id", selectStmt.GetColumnInfo(2).GetProperty()->GetName().c_str()); + ASSERT_EQ(BE_SQLITE_ROW, selectStmt.Step()); + ASSERT_STREQ("str4", selectStmt.GetValueText(0)) << "str_prop"; + ASSERT_EQ(4, selectStmt.GetValueInt(1)) << "int_prop"; + ASSERT_EQ(listOfIds[3], selectStmt.GetValueId(2)) << "id"; + + ASSERT_EQ(BE_SQLITE_ROW, selectStmt.Step()); + ASSERT_STREQ("str7", selectStmt.GetValueText(0)) << "str_prop"; + ASSERT_EQ(7, selectStmt.GetValueInt(1)) << "int_prop"; + ASSERT_EQ(listOfIds[6], selectStmt.GetValueId(2)) << "id"; + + ASSERT_EQ(BE_SQLITE_ROW, selectStmt.Step()); + ASSERT_STREQ("str8", selectStmt.GetValueText(0)) << "str_prop"; + ASSERT_EQ(8, selectStmt.GetValueInt(1)) << "int_prop"; + ASSERT_EQ(listOfIds[7], selectStmt.GetValueId(2)) << "id"; + + ASSERT_EQ(BE_SQLITE_DONE, selectStmt.Step()); + + ASSERT_STREQ("SELECT [test].[str_prop],[test].[int_prop],v.id FROM (SELECT [Id] ECInstanceId,88 ECClassId,[str_prop],[int_prop] FROM [main].[ts_A]) [test] INNER JOIN IdSet(:_ecdb_sqlparam_ix1_col1) v ON [test].[ECInstanceId]=v.id ", selectStmt.GetNativeSql()); + } + if("testing inner join with IdSet") + { + ECSqlStatement selectStmt; + ASSERT_EQ(ECSqlStatus::Success, selectStmt.Prepare(m_ecdb, "Select test.str_prop, test.int_prop, v.id from ts.A test INNER JOIN ECVLib.IdSet(?) v on test.ECInstanceId = v.id")); + IECSqlBinder& binder = selectStmt.GetBinder(1); + ASSERT_EQ(ECSqlStatus::Success,binder.AddArrayElement().BindId(listOfIds[3])); + ASSERT_EQ(ECSqlStatus::Success,binder.AddArrayElement().BindId(listOfIds[6])); + ASSERT_EQ(ECSqlStatus::Success,binder.AddArrayElement().BindId(listOfIds[7])); + ASSERT_STREQ("str_prop", selectStmt.GetColumnInfo(0).GetProperty()->GetName().c_str()); + ASSERT_STREQ("int_prop", selectStmt.GetColumnInfo(1).GetProperty()->GetName().c_str()); + ASSERT_STREQ("id", selectStmt.GetColumnInfo(2).GetProperty()->GetName().c_str()); + ASSERT_EQ(BE_SQLITE_ROW, selectStmt.Step()); + ASSERT_STREQ("str4", selectStmt.GetValueText(0)) << "str_prop"; + ASSERT_EQ(4, selectStmt.GetValueInt(1)) << "int_prop"; + ASSERT_EQ(listOfIds[3], selectStmt.GetValueId(2)) << "id"; + + ASSERT_EQ(BE_SQLITE_ROW, selectStmt.Step()); + ASSERT_STREQ("str7", selectStmt.GetValueText(0)) << "str_prop"; + ASSERT_EQ(7, selectStmt.GetValueInt(1)) << "int_prop"; + ASSERT_EQ(listOfIds[6], selectStmt.GetValueId(2)) << "id"; + + ASSERT_EQ(BE_SQLITE_ROW, selectStmt.Step()); + ASSERT_STREQ("str8", selectStmt.GetValueText(0)) << "str_prop"; + ASSERT_EQ(8, selectStmt.GetValueInt(1)) << "int_prop"; + ASSERT_EQ(listOfIds[7], selectStmt.GetValueId(2)) << "id"; + + ASSERT_EQ(BE_SQLITE_DONE, selectStmt.Step()); + ASSERT_STREQ("SELECT [test].[str_prop],[test].[int_prop],v.id FROM (SELECT [Id] ECInstanceId,88 ECClassId,[str_prop],[int_prop] FROM [main].[ts_A]) [test] INNER JOIN IdSet(:_ecdb_sqlparam_ix1_col1) v ON [test].[ECInstanceId]=v.id ", selectStmt.GetNativeSql()); + } + if("testing right outer join with IdSet") + { + ECSqlStatement selectStmt; + ASSERT_EQ(ECSqlStatus::Success, selectStmt.Prepare(m_ecdb, "Select test.str_prop, test.int_prop, v.id from ts.A test RIGHT OUTER JOIN ECVLib.IdSet(?) v on test.ECInstanceId = v.id")); + IECSqlBinder& binder = selectStmt.GetBinder(1); + ASSERT_EQ(ECSqlStatus::Success,binder.AddArrayElement().BindId(listOfIds[3])); + ASSERT_EQ(ECSqlStatus::Success,binder.AddArrayElement().BindId(listOfIds[6])); + ASSERT_EQ(ECSqlStatus::Success,binder.AddArrayElement().BindId(listOfIds[7])); + ASSERT_STREQ("str_prop", selectStmt.GetColumnInfo(0).GetProperty()->GetName().c_str()); + ASSERT_STREQ("int_prop", selectStmt.GetColumnInfo(1).GetProperty()->GetName().c_str()); + ASSERT_STREQ("id", selectStmt.GetColumnInfo(2).GetProperty()->GetName().c_str()); + ASSERT_EQ(BE_SQLITE_ROW, selectStmt.Step()); + ASSERT_STREQ("str4", selectStmt.GetValueText(0)) << "str_prop"; + ASSERT_EQ(4, selectStmt.GetValueInt(1)) << "int_prop"; + ASSERT_EQ(listOfIds[3], selectStmt.GetValueId(2)) << "id"; + + ASSERT_EQ(BE_SQLITE_ROW, selectStmt.Step()); + ASSERT_STREQ("str7", selectStmt.GetValueText(0)) << "str_prop"; + ASSERT_EQ(7, selectStmt.GetValueInt(1)) << "int_prop"; + ASSERT_EQ(listOfIds[6], selectStmt.GetValueId(2)) << "id"; + + ASSERT_EQ(BE_SQLITE_ROW, selectStmt.Step()); + ASSERT_STREQ("str8", selectStmt.GetValueText(0)) << "str_prop"; + ASSERT_EQ(8, selectStmt.GetValueInt(1)) << "int_prop"; + ASSERT_EQ(listOfIds[7], selectStmt.GetValueId(2)) << "id"; + + ASSERT_EQ(BE_SQLITE_DONE, selectStmt.Step()); + ASSERT_STREQ("SELECT [test].[str_prop],[test].[int_prop],v.id FROM (SELECT [Id] ECInstanceId,88 ECClassId,[str_prop],[int_prop] FROM [main].[ts_A]) [test] RIGHT OUTER JOIN IdSet(:_ecdb_sqlparam_ix1_col1) v ON [test].[ECInstanceId]=v.id ", selectStmt.GetNativeSql()); + } + if("testing left outer join with IdSet") + { + ECSqlStatement selectStmt; + ASSERT_EQ(ECSqlStatus::Success, selectStmt.Prepare(m_ecdb, "Select test.str_prop, test.int_prop, v.id from ts.A test LEFT OUTER JOIN ECVLib.IdSet(?) v on test.ECInstanceId = v.id")); + IECSqlBinder& binder = selectStmt.GetBinder(1); + ASSERT_EQ(ECSqlStatus::Success,binder.AddArrayElement().BindId(listOfIds[3])); + ASSERT_EQ(ECSqlStatus::Success,binder.AddArrayElement().BindId(listOfIds[6])); + ASSERT_EQ(ECSqlStatus::Success,binder.AddArrayElement().BindId(listOfIds[7])); + ASSERT_STREQ("str_prop", selectStmt.GetColumnInfo(0).GetProperty()->GetName().c_str()); + ASSERT_STREQ("int_prop", selectStmt.GetColumnInfo(1).GetProperty()->GetName().c_str()); + ASSERT_STREQ("id", selectStmt.GetColumnInfo(2).GetProperty()->GetName().c_str()); + + ASSERT_EQ(BE_SQLITE_ROW, selectStmt.Step()); + ASSERT_STREQ("str1", selectStmt.GetValueText(0)) << "str_prop"; + ASSERT_EQ(1, selectStmt.GetValueInt(1)) << "int_prop"; + ASSERT_EQ(selectStmt.IsValueNull(2), true) << "id"; + + ASSERT_EQ(BE_SQLITE_ROW, selectStmt.Step()); + ASSERT_STREQ("str2", selectStmt.GetValueText(0)) << "str_prop"; + ASSERT_EQ(2, selectStmt.GetValueInt(1)) << "int_prop"; + ASSERT_EQ(selectStmt.IsValueNull(2), true) << "id"; + + ASSERT_EQ(BE_SQLITE_ROW, selectStmt.Step()); + ASSERT_STREQ("str3", selectStmt.GetValueText(0)) << "str_prop"; + ASSERT_EQ(3, selectStmt.GetValueInt(1)) << "int_prop"; + ASSERT_EQ(selectStmt.IsValueNull(2), true) << "id"; + + ASSERT_EQ(BE_SQLITE_ROW, selectStmt.Step()); + ASSERT_STREQ("str4", selectStmt.GetValueText(0)) << "str_prop"; + ASSERT_EQ(4, selectStmt.GetValueInt(1)) << "int_prop"; + ASSERT_EQ(listOfIds[3], selectStmt.GetValueId(2)) << "id"; + + ASSERT_EQ(BE_SQLITE_ROW, selectStmt.Step()); + ASSERT_STREQ("str5", selectStmt.GetValueText(0)) << "str_prop"; + ASSERT_EQ(5, selectStmt.GetValueInt(1)) << "int_prop"; + ASSERT_EQ(selectStmt.IsValueNull(2), true) << "id"; + + ASSERT_EQ(BE_SQLITE_ROW, selectStmt.Step()); + ASSERT_STREQ("str6", selectStmt.GetValueText(0)) << "str_prop"; + ASSERT_EQ(6, selectStmt.GetValueInt(1)) << "int_prop"; + ASSERT_EQ(selectStmt.IsValueNull(2), true) << "id"; + + ASSERT_EQ(BE_SQLITE_ROW, selectStmt.Step()); + ASSERT_STREQ("str7", selectStmt.GetValueText(0)) << "str_prop"; + ASSERT_EQ(7, selectStmt.GetValueInt(1)) << "int_prop"; + ASSERT_EQ(listOfIds[6], selectStmt.GetValueId(2)) << "id"; + + ASSERT_EQ(BE_SQLITE_ROW, selectStmt.Step()); + ASSERT_STREQ("str8", selectStmt.GetValueText(0)) << "str_prop"; + ASSERT_EQ(8, selectStmt.GetValueInt(1)) << "int_prop"; + ASSERT_EQ(listOfIds[7], selectStmt.GetValueId(2)) << "id"; + + ASSERT_EQ(BE_SQLITE_ROW, selectStmt.Step()); + ASSERT_STREQ("str9", selectStmt.GetValueText(0)) << "str_prop"; + ASSERT_EQ(9, selectStmt.GetValueInt(1)) << "int_prop"; + ASSERT_EQ(selectStmt.IsValueNull(2), true) << "id"; + + ASSERT_EQ(BE_SQLITE_ROW, selectStmt.Step()); + ASSERT_STREQ("str10", selectStmt.GetValueText(0)) << "str_prop"; + ASSERT_EQ(10, selectStmt.GetValueInt(1)) << "int_prop"; + ASSERT_EQ(selectStmt.IsValueNull(2), true) << "id"; + + ASSERT_EQ(BE_SQLITE_DONE, selectStmt.Step()); + ASSERT_STREQ("SELECT [test].[str_prop],[test].[int_prop],v.id FROM (SELECT [Id] ECInstanceId,88 ECClassId,[str_prop],[int_prop] FROM [main].[ts_A]) [test] LEFT OUTER JOIN IdSet(:_ecdb_sqlparam_ix1_col1) v ON [test].[ECInstanceId]=v.id ", selectStmt.GetNativeSql()); + } + if("testing left outer join with IdSet") + { + ECSqlStatement selectStmt; + ASSERT_EQ(ECSqlStatus::InvalidECSql, selectStmt.Prepare(m_ecdb, "Select test.str_prop, test.int_prop, v.id from ts.A test OUTER JOIN ECVLib.IdSet(?) v on test.ECInstanceId = v.id")); + } +} + END_ECDBUNITTESTS_NAMESPACE From 9d37a1f1f6c07212ee524ccd1a3153bca54269b2 Mon Sep 17 00:00:00 2001 From: soham-bentley <177123878+soham-bentley@users.noreply.github.com> Date: Mon, 20 Jan 2025 17:44:06 +0530 Subject: [PATCH 36/43] More Performance Tests added --- .../Performance/PerformanceIdSetTests.cpp | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/iModelCore/ECDb/Tests/Performance/PerformanceIdSetTests.cpp b/iModelCore/ECDb/Tests/Performance/PerformanceIdSetTests.cpp index e9227e3fe55..8f7beeb8b3f 100644 --- a/iModelCore/ECDb/Tests/Performance/PerformanceIdSetTests.cpp +++ b/iModelCore/ECDb/Tests/Performance/PerformanceIdSetTests.cpp @@ -177,4 +177,78 @@ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet_with LOGTODB(TEST_DETAILS, timer_IdSet.GetElapsedSeconds(), i, SqlPrintfString("Statement: \"%s\"", stmt_With_IdSet.GetECSql())); } } + +//--------------------------------------------------------------------------------------- +// @bsiclass +//+---------------+---------------+---------------+---------------+---------------+------ +TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenJoinedQuery_and_QueryWithWhereClause_with_IdSet) + { + ASSERT_EQ(BentleyStatus::SUCCESS, SetupECDb("SimpleComparisonBetweenJoinedQuery_and_QueryWithWhereClause_with_IdSet.ecdb", SchemaItem(R"xml( + + + + + )xml"))); + + ECSqlStatement insert_stmt; + ASSERT_EQ(ECSqlStatus::Success, insert_stmt.Prepare(m_ecdb, "insert into ts.Foo(UnIndexed_Prop) values(?)")); + for(int i = 0;i<1000000;i++) + { + insert_stmt.ClearBindings(); + insert_stmt.Reset(); + int rand = (i+1) % 70000; + insert_stmt.BindInt64(1, rand); + ASSERT_EQ(BE_SQLITE_DONE, insert_stmt.Step()); + } + + std::vector v = {"ECInstanceId", "UnIndexed_Prop"}; + for(auto& str: v) + { + ECSqlStatement stmt_With_Join; + ECSqlStatement stmt_With_WhereClause; + IdSet emptyIdset; + + ASSERT_EQ(ECSqlStatus::Success, stmt_With_Join.Prepare(m_ecdb,SqlPrintfString("select %s from ts.Foo test INNER JOIN ECVLib.IdSet(?) v ON test.%s = v.id group by %s", str.c_str(), str.c_str(), str.c_str()))); + ASSERT_EQ(ECSqlStatus::Success, stmt_With_WhereClause.Prepare(m_ecdb,SqlPrintfString("select %s from ECVLib.IdSet(?), ts.Foo where %s = id group by %s", str.c_str(), str.c_str(), str.c_str()))); + + IECSqlBinder& binderForJoin = stmt_With_Join.GetBinder(1); + for(int i = 0;i<2000;i++) + { + int randNum = i+1; + ASSERT_EQ(ECSqlStatus::Success, binderForJoin.AddArrayElement().BindInt64(randNum)); + emptyIdset.insert(BeInt64Id(randNum)); + } + + IECSqlBinder& binderForWhereClause = stmt_With_WhereClause.GetBinder(1); + for(int i = 0;i<2000;i++) + { + int randNum = i+1; + ASSERT_EQ(ECSqlStatus::Success, binderForWhereClause.AddArrayElement().BindInt64(randNum)); + } + + auto iterator_set = emptyIdset.begin(); + int i = 0; + StopWatch timer_Join_Query(true); + while(stmt_With_Join.Step() == BE_SQLITE_ROW) + { + ASSERT_EQ((*iterator_set).GetValue(), stmt_With_Join.GetValueInt64(0)); + ++iterator_set; + i++; + } + timer_Join_Query.Stop(); + LOGTODB(TEST_DETAILS, timer_Join_Query.GetElapsedSeconds(), i, SqlPrintfString("Statement: \"%s\"", stmt_With_Join.GetECSql())); + + iterator_set = emptyIdset.begin(); + i = 0; + StopWatch timer_Query_With_WhereClause(true); + while(stmt_With_WhereClause.Step() == BE_SQLITE_ROW) + { + ASSERT_EQ((*iterator_set).GetValue(), stmt_With_WhereClause.GetValueInt64(0)); + ++iterator_set; + i++; + } + timer_Query_With_WhereClause.Stop(); + LOGTODB(TEST_DETAILS, timer_Query_With_WhereClause.GetElapsedSeconds(), i, SqlPrintfString("Statement: \"%s\"", stmt_With_WhereClause.GetECSql())); + } + } END_ECDBUNITTESTS_NAMESPACE \ No newline at end of file From df39db11392ab53483457d88e4c59ba8f696a585 Mon Sep 17 00:00:00 2001 From: soham-bentley <177123878+soham-bentley@users.noreply.github.com> Date: Wed, 22 Jan 2025 15:53:33 +0530 Subject: [PATCH 37/43] indentation issue solved --- iModelCore/ECDb/ECDb/BuiltInVTabs.cpp | 30 +- .../ECDb/ECDb/ConcurrentQueryManagerImpl.cpp | 15 +- .../ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp | 2 +- .../ECDb/ECSql/ECSqlPreparedStatement.cpp | 4 +- .../ECSql/PragmaECSqlPreparedStatement.cpp | 4 +- iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp | 20 +- .../NonPublished/ConcurrentQueryTest_V2.cpp | 16 +- .../ECDbIdSetVirtualTableTests.cpp | 366 +++++++++--------- .../Performance/PerformanceIdSetTests.cpp | 84 ++-- .../api_package/ts/package-lock.json | 6 +- 10 files changed, 268 insertions(+), 279 deletions(-) diff --git a/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp b/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp index 43145ed9861..f3f9d9c28c5 100644 --- a/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp +++ b/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp @@ -185,20 +185,19 @@ DbResult IdSetModule::IdSetTable::IdSetCursor::GetColumn(int i, Context& ctx) { // @bsimethod //--------------------------------------------------------------------------------------- DbResult IdSetModule::IdSetTable::IdSetCursor::FilterJSONStringIntoArray(BeJsDocument& doc) { - if(!doc.isArray()) - { + if(!doc.isArray()) { GetTable().SetError("IdSet vtab: The argument should be a valid JSON array of ids"); return BE_SQLITE_ERROR; } bool flag = doc.ForEachArrayMember([&](BeJsValue::ArrayIndex a, BeJsConst k1) - { - if(BE_SQLITE_OK != FilterJSONBasedOnType(k1)) { + if(BE_SQLITE_OK != FilterJSONBasedOnType(k1)) + { GetTable().SetError(SqlPrintfString("IdSet vtab: The element with index %u is invalid", a)); return true; - } + } return false; - }); + }); if(flag) return BE_SQLITE_ERROR; return BE_SQLITE_OK; @@ -209,19 +208,16 @@ DbResult IdSetModule::IdSetTable::IdSetCursor::FilterJSONStringIntoArray(BeJsDoc // @bsimethod //--------------------------------------------------------------------------------------- DbResult IdSetModule::IdSetTable::IdSetCursor::FilterJSONBasedOnType(BeJsConst& val) { - if(val.isNull()) - { + if(val.isNull()) { return BE_SQLITE_ERROR; } - else if(val.isNumeric()) - { + else if(val.isNumeric()) { uint64_t id = val.GetUInt64(); if(id == 0) return BE_SQLITE_ERROR; m_idSet.insert(id); } - else if(val.isString()) - { + else if(val.isString()) { uint64_t id; BentleyStatus status = BeStringUtilities::ParseUInt64(id, val.ToUtf8CP()); if(status != BentleyStatus::SUCCESS) @@ -253,14 +249,12 @@ DbResult IdSetModule::IdSetTable::IdSetCursor::Filter(int idxNum, const char *id } else { Reset(); } - if(recompute) - { + if(recompute) { m_idSet.clear(); BeJsDocument doc; doc.Parse(m_text.c_str()); - if(FilterJSONStringIntoArray(doc) != BE_SQLITE_OK) - { + if(FilterJSONStringIntoArray(doc) != BE_SQLITE_OK) { Reset(); m_index = m_idSet.begin(); return BE_SQLITE_ERROR; @@ -290,7 +284,7 @@ DbResult IdSetModule::IdSetTable::BestIndex(IndexInfo& indexInfo) { aIdx[0] = aIdx[1] = -1; int nConstraint = indexInfo.GetConstraintCount(); - for(i=0; i> idSet = std::make_shared>(param.GetValueIdSet()); st = stmt.BindVirtualSet(index, idSet); } - else if(binderInfo.GetType() == BinderInfo::BinderType::Array && binderInfo.IsForIdSet()) - { + else if(binderInfo.GetType() == BinderInfo::BinderType::Array && binderInfo.IsForIdSet()) { bool allElementsAdded = true; IECSqlBinder& binder = stmt.GetBinder(index); IdSet set(param.GetValueIdSet()); - for(auto& ids: set) - { - if(!ids.IsValid()) - { + for(auto& ids: set) { + if(!ids.IsValid()) { allElementsAdded = false; break; } st = binder.AddArrayElement().BindInt64((int64_t) ids.GetValue()); - if(!st.IsSuccess()) - { + if(!st.IsSuccess()) { allElementsAdded = false; break; } diff --git a/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp b/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp index 875b6818b23..112a09e597f 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ArrayECSqlBinder.cpp @@ -585,7 +585,7 @@ ECSqlStatus ArrayECSqlBinder::JsonValueBinder::FailIfInvalid() const //--------------------------------------------------------------------------------------- BinderInfo& ArrayECSqlBinder::JsonValueBinder::_GetBinderInfo() { - return m_binderInfo; + return m_binderInfo; } diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp index 73c41066b02..4bad0c27320 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp @@ -212,10 +212,10 @@ DbResult SingleECSqlPreparedStatement::DoStep() return BE_SQLITE_ERROR; if(m_sqliteStatement.GetStatementState() == StatementState::VDBE_READY_STATE) - { + { if (!m_parameterMap.OnBeforeFirstStep().IsSuccess()) return BE_SQLITE_ERROR; - } + } const DbResult nativeSqlStatus = m_sqliteStatement.Step(); diff --git a/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.cpp b/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.cpp index d865ae72295..57f9e4e51f9 100644 --- a/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.cpp @@ -637,10 +637,10 @@ DbResult PragmaECSqlPreparedStatement::DoStep() { return BE_SQLITE_ERROR; if(m_isFirstStep) - { + { if (!m_parameterMap.OnBeforeFirstStep().IsSuccess()) return BE_SQLITE_ERROR; - } + } DbResult res = m_resultSet->Step(); if((res == BE_SQLITE_DONE || res == BE_SQLITE_ROW) && m_isFirstStep) diff --git a/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp b/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp index 097563469b2..09ea0834210 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp @@ -818,10 +818,10 @@ Exp::FinalizeParseStatus MemberFunctionCallExp::_FinalizeParsing(ECSqlParseConte { if (this->IsTableValuedFunc()) { if(m_functionName.EqualsIAscii(IdSetModule::NAME)) - { + { ValueExp const* argExp = GetArgument(0); if(argExp == nullptr) - { + { ctx.Issues().ReportV( IssueSeverity::Error, IssueCategory::BusinessProperties, @@ -831,10 +831,10 @@ Exp::FinalizeParseStatus MemberFunctionCallExp::_FinalizeParsing(ECSqlParseConte m_functionName.c_str() ); return Exp::FinalizeParseStatus::Error; - } + } ECSqlTypeInfo typeInfo = argExp->GetTypeInfo(); if (!typeInfo.IsPrimitive() && !typeInfo.IsUnset()) - { + { ctx.Issues().ReportV( IssueSeverity::Error, IssueCategory::BusinessProperties, @@ -844,12 +844,12 @@ Exp::FinalizeParseStatus MemberFunctionCallExp::_FinalizeParsing(ECSqlParseConte m_functionName.c_str() ); return Exp::FinalizeParseStatus::Error; - } + } if(typeInfo.IsPrimitive()) - { + { ECN::PrimitiveType primitiveType = typeInfo.GetPrimitiveType(); if(primitiveType != ECN::PrimitiveType::PRIMITIVETYPE_String) - { + { ctx.Issues().ReportV( IssueSeverity::Error, IssueCategory::BusinessProperties, @@ -859,9 +859,9 @@ Exp::FinalizeParseStatus MemberFunctionCallExp::_FinalizeParsing(ECSqlParseConte m_functionName.c_str() ); return Exp::FinalizeParseStatus::Error; + } } } - } return FinalizeParseStatus::Completed; } FunctionSignature const* funcSig = FunctionSignatureSet::GetInstance().Find(m_functionName.c_str()); @@ -961,10 +961,10 @@ Utf8String MemberFunctionCallExp::_ToString() const bool MemberFunctionCallExp::_TryDetermineParameterExpType(ECSqlParseContext& ctx, ParameterExp& parameterExp) const { if(this->IsTableValuedFunc() && m_functionName.EqualsIAscii(IdSetModule::NAME)) - { + { parameterExp.SetTargetExpInfo(ECSqlTypeInfo::CreatePrimitive(ECN::PRIMITIVETYPE_Long, true, EXTENDEDTYPENAME_Id)); return true; - } + } //we don't have metadata about function args, so use a default type if the arg is a parameter parameterExp.SetTargetExpInfo(ECSqlTypeInfo::CreatePrimitive(ECN::PRIMITIVETYPE_Double)); return true; diff --git a/iModelCore/ECDb/Tests/NonPublished/ConcurrentQueryTest_V2.cpp b/iModelCore/ECDb/Tests/NonPublished/ConcurrentQueryTest_V2.cpp index 0744dce2ecc..c41d41d7617 100644 --- a/iModelCore/ECDb/Tests/NonPublished/ConcurrentQueryTest_V2.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/ConcurrentQueryTest_V2.cpp @@ -1153,7 +1153,7 @@ TEST_F(ConcurrentQueryFixture, CommentAtEndOfECSql) { TEST_F(ConcurrentQueryFixture, ReaderBindingForIdSetVirtualTable) { ASSERT_EQ(DbResult::BE_SQLITE_OK, SetupECDb("ConcurrentQuery_Simple.ecdb")); - { + { auto& mgr = ConcurrentQueryMgr::GetInstance(m_ecdb); BeIdSet idSet; idSet.insert(BeInt64Id(10)); @@ -1173,8 +1173,8 @@ TEST_F(ConcurrentQueryFixture, ReaderBindingForIdSetVirtualTable) { vsRowCount++; } ASSERT_EQ(vsRowCount,4); - } - { + } + { auto& mgr = ConcurrentQueryMgr::GetInstance(m_ecdb); BeIdSet idSet; idSet.insert(BeInt64Id(10)); @@ -1194,8 +1194,8 @@ TEST_F(ConcurrentQueryFixture, ReaderBindingForIdSetVirtualTable) { vsRowCount++; } ASSERT_EQ(vsRowCount,4); - } - { + } + { auto& mgr = ConcurrentQueryMgr::GetInstance(m_ecdb); int vsRowCount = 0; @@ -1203,11 +1203,11 @@ TEST_F(ConcurrentQueryFixture, ReaderBindingForIdSetVirtualTable) { ECSqlParams().BindId(1, BeInt64Id(33))); while(vsReaderIdSet.Next()) - { + { vsRowCount++; - } + } ASSERT_EQ(vsRowCount,0); - } + } } diff --git a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp index a4939ded263..de85b00aabc 100644 --- a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp @@ -17,110 +17,110 @@ struct ECDbIdSetVirtualTableTestFixture : ECDbTestFixture {}; //+---------------+---------------+---------------+---------------+---------------+------ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetModuleTest) { ASSERT_EQ(BE_SQLITE_OK, SetupECDb("vtab.ecdb")); - { + { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet('[1,2,3,4,5]')")); int i = 0; while (stmt.Step() == BE_SQLITE_ROW) - { + { ASSERT_EQ((1+i++), stmt.GetValueInt64(0)); - } + } ASSERT_EQ(i, 5); - } - { + } + { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet('[1,2,3,4,5]') where id = 1")); int i = 0; while (stmt.Step() == BE_SQLITE_ROW) - { + { ASSERT_EQ((1+i++), stmt.GetValueInt64(0)); - } + } ASSERT_EQ(i, 1); - } - { + } + { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet('[1,2,3,4,5]') where id > 1")); int i = 1; while (stmt.Step() == BE_SQLITE_ROW) - { + { ASSERT_EQ((1+i++), stmt.GetValueInt64(0)); - } + } ASSERT_EQ(i, 5); - } - { + } + { std::vector hexIds = std::vector{"0x1", "0x2", "0x3", "4", "5"}; ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet(?)")); IECSqlBinder& arrayBinder = stmt.GetBinder(1); for(int i =0;i> bi_array = { {0x48, 0x65, 0x6}, {0x48, 0x65, 0x6}, @@ -238,119 +238,119 @@ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetModuleTest) { // Binary is Binded so should be empty table and error should be thrown ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); - } - { + } + { const auto dt = DateTime(DateTime::Kind::Unspecified, 2017, 1, 17, 0, 0); const auto dtUtc = DateTime(DateTime::Kind::Utc, 2018, 2, 17, 0, 0); ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet(?)")); IECSqlBinder& arrayBinder = stmt.GetBinder(1); for(int i = 0;i<=1;i++) - { + { ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindDateTime(dt)); ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindDateTime(dtUtc)); - } + } // EmptyArray is Binded so should be empty table and should log error because the ultimate json text which will be binded will be an empty json array and that is not allowed ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); - } - { + } + { auto geom = IGeometry::Create(ICurvePrimitive::CreateLine(DSegment3d::From(0.0, 0.0, 0.0, 1.0, 1.0, 1.0))); ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet(?)")); IECSqlBinder& arrayBinder = stmt.GetBinder(1); for(int i = 0;i<=1;i++) - { + { ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindGeometry(*geom)); - } + } // Binary is Binded because BindGeometry internally calls so should be empty table and error should be thrown ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); - } - { + } + { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet(?)")); IECSqlBinder& arrayBinder = stmt.GetBinder(1); for(int i = 0;i<=1;i++) - { + { ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindText("ABC",IECSqlBinder::MakeCopy::No)); - } + } // EmptyArray is Binded so should be empty table and should log error because the ultimate json text which will be binded will be an empty json array and that is not allowed ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); - } - { + } + { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet(?)")); IECSqlBinder& arrayBinder = stmt.GetBinder(1); for(int i = 0;i<=1;i++) - { + { ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindText("[abc]",IECSqlBinder::MakeCopy::No)); - } + } // EmptyArray is Binded so should be empty table and should log error because the ultimate json text which will be binded will be an empty json array and that is not allowed ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); - } - { + } + { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet(?)")); IECSqlBinder& arrayBinder = stmt.GetBinder(1); for(int i = 0;i<=1;i++) - { + { ASSERT_EQ(ECSqlStatus::Error, arrayBinder.AddArrayElement().BindText("[\"abc\"]",IECSqlBinder::MakeCopy::No)); - } + } // EmptyArray is Binded so should be empty table and should log error because the ultimate json text which will be binded will be an empty json array and that is not allowed ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); - } - { + } + { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT x FROM ECVLib.IdSet('[1,2,3,4,5]'), (with cte(x) as(select ECInstanceId from meta.ECClassDef) select x from cte) where id = x group by x")); int i = 1; while (stmt.Step() == BE_SQLITE_ROW) - { + { ASSERT_EQ((i++), stmt.GetValueInt64(0)); - } + } ASSERT_EQ(i, 6); - } - { + } + { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT x FROM (with cte(x) as(select ECInstanceId from meta.ECClassDef) select x from cte), ECVLib.IdSet('[1,2,3,4,5]') where id = x group by x")); int i = 1; while (stmt.Step() == BE_SQLITE_ROW) - { + { ASSERT_EQ((i++), stmt.GetValueInt64(0)); - } + } ASSERT_EQ(i, 6); - } - { + } + { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT ECInstanceId FROM ECVLib.IdSet('[1,2,3,4,5]'), meta.ECClassDef where ECInstanceId = id group by ECInstanceId")); ASSERT_EQ(true, m_ecdb.ExplainQuery(stmt.GetNativeSql(), true).Contains("SCAN IdSet VIRTUAL TABLE INDEX 1")); ASSERT_EQ(true, m_ecdb.ExplainQuery(stmt.GetNativeSql(), true).Contains("SEARCH main.ec_Class USING INTEGER PRIMARY KEY (rowid=?)")); - } - { + } + { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT ECClassId FROM ECVLib.IdSet('[1,2,3,4,5]'), meta.ECClassDef where ECClassId = id group by ECClassId")); ASSERT_EQ(true, m_ecdb.ExplainQuery(stmt.GetNativeSql(), true).Contains("SCAN IdSet VIRTUAL TABLE INDEX 1")); ASSERT_EQ(true, m_ecdb.ExplainQuery(stmt.GetNativeSql(), true).Contains("SCAN main.ec_Class USING COVERING INDEX ix_ec_Class_Name")); - } - { + } + { std::vector hexIds = std::vector{"0x1", "0x2", "0x3", "4", "5"}; ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT ECInstanceId FROM ECVLib.IdSet(?), meta.ECClassDef where ECInstanceId = id group by ECInstanceId")); IECSqlBinder& arrayBinder = stmt.GetBinder(1); for(int i =0;i ids = std::vector{0,1,1,2}; ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet(?)")); IECSqlBinder& binder = stmt.GetBinder(1); for(auto& i : ids) - { + { ASSERT_EQ(ECSqlStatus::Success, binder.AddArrayElement().BindInt(i)); - } + } // 0 is not allowed so empty table and dhould log error ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); - } - { + } + { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet('[\"-1\",\"-2\",\"3\",\"-4\",\"5\"]')")); // negative values are not allowed so empty table and should log error ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); - } - { + } + { std::vector stringIds = std::vector{"-1", "-2", "3", "-4", "5"}; ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet(?)")); IECSqlBinder& binder = stmt.GetBinder(1); for(auto& i : stringIds) - { + { if(i.EqualsIAscii("3") || i.EqualsIAscii("5")) ASSERT_EQ(ECSqlStatus::Success, binder.AddArrayElement().BindText(i.c_str(), IECSqlBinder::MakeCopy::No)); else ASSERT_EQ(ECSqlStatus::Error, binder.AddArrayElement().BindText(i.c_str(), IECSqlBinder::MakeCopy::No)); - } + } // Binding negative values will fail so for the negative values binder.AddArrayElement().BindText() will be empty array element which are not allowed so empty table and should log error ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); - } - { + } + { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet('[\"0xFFFFFFFF\",3,4,5]')")); int i = 3; while (i<=5) - { + { ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()); ASSERT_EQ(i, stmt.GetValueInt64(0)); i++; + } } - } - { + { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet('[\"0x0\",3,4,5]')")); // 0 values are not allowed so empty table and should log error ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); - } - { + } + { std::vector hexIds = std::vector{"0x1", "0x2", "0x3", "4", "5"}; ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT ECInstanceId FROM ECVLib.IdSet(?), meta.ECClassDef where ECInstanceId = id group by ECInstanceId")); IECSqlBinder& arrayBinder = stmt.GetBinder(1); for(int i =0;i ids = std::vector{"0x1", "0x2", "0x3", "4.5", "5.5"}; ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT ECInstanceId FROM ECVLib.IdSet(?), meta.ECClassDef where ECInstanceId = id group by ECInstanceId")); IECSqlBinder& arrayBinder = stmt.GetBinder(1); for(int i =0;i dec_ids = std::vector{1, 2, 4.5, 3, 5.5}; IECSqlBinder& arrayBinder_two = stmt.GetBinder(1); for(int i =0;i ids = std::vector{"0x1", "0x2", "0x3", "4.5", "5.5"}; ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT ECInstanceId FROM ECVLib.IdSet(?), meta.ECClassDef where ECInstanceId = id group by ECInstanceId")); IECSqlBinder& arrayBinder = stmt.GetBinder(1); for(int i =0;i dec_ids = std::vector{1, 2, 4.0, 3, 5.0}; IECSqlBinder& arrayBinder_two = stmt.GetBinder(1); for(int i =0;i dec_ids = std::vector{1, 2, 4.0, 3, 5.0}; IECSqlBinder& arrayBinder = stmt.GetBinder(1); for(int i =0;i dec_ids = std::vector{0,1,2,3.0}; IECSqlBinder& arrayBinder = stmt.GetBinder(1); for(int i =0;i dec_ids = std::vector{"0","1","2","3.0"}; IECSqlBinder& arrayBinder = stmt.GetBinder(1); for(int i =0;i v = {"0x373","0x38F", "0x32", "0x01"}; IdSet emptyIdset; for(auto str: v) - { + { emptyIdset.insert(BeInt64Id(BeStringUtilities::ParseHex(str.c_str()))); - } + } std::shared_ptr> idSetPtr = std::make_shared>(emptyIdset); ASSERT_EQ(ECSqlStatus::Success, stmt_With_InvirtualSet.Prepare(m_ecdb, "with cnt(x) as (values(1) union select x+1 from cnt where x < 1000000 ) select * from cnt where invirtualset(?, x)")); ASSERT_EQ(ECSqlStatus::Success, stmt_With_InvirtualSet.BindVirtualSet(1, idSetPtr)); @@ -30,19 +30,19 @@ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet) ASSERT_EQ(ECSqlStatus::Success, stmt_With_IdSet.Prepare(m_ecdb, "select x from ECVLib.IdSet(?), (with cnt(x) as (values(1) union select x+1 from cnt where x < 1000000 ) select * from cnt) where id = x")); IECSqlBinder& binder = stmt_With_IdSet.GetBinder(1); for(auto str: v) - { + { ASSERT_EQ(ECSqlStatus::Success, binder.AddArrayElement().BindText(str.c_str(), IECSqlBinder::MakeCopy::No)); - } + } auto iterator_set = emptyIdset.begin(); int i = 0; StopWatch timer_InVirtual_Set(true); while(stmt_With_InvirtualSet.Step() == BE_SQLITE_ROW) - { + { ASSERT_EQ((*iterator_set).GetValue(), stmt_With_InvirtualSet.GetValueInt64(0)); ++iterator_set; i++; - } + } timer_InVirtual_Set.Stop(); LOGTODB(TEST_DETAILS, timer_InVirtual_Set.GetElapsedSeconds(), i, SqlPrintfString("Statement: \"%s\"", stmt_With_InvirtualSet.GetECSql())); @@ -50,20 +50,20 @@ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet) i = 0; StopWatch timer_IdSet(true); while(stmt_With_IdSet.Step() == BE_SQLITE_ROW) - { + { ASSERT_EQ((*iterator_set).GetValue(), stmt_With_IdSet.GetValueInt64(0)); ++iterator_set; i++; - } + } timer_IdSet.Stop(); LOGTODB(TEST_DETAILS, timer_IdSet.GetElapsedSeconds(), i, SqlPrintfString("Statement: \"%s\"", stmt_With_IdSet.GetECSql())); - } + } //--------------------------------------------------------------------------------------- // @bsiclass //+---------------+---------------+---------------+---------------+---------------+------ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet_with_large_set) - { + { ASSERT_EQ(BE_SQLITE_OK, SetupECDb("SimpleComparisonBetweenInVirtualSet_and_IdSet_with_large_set.ecdb")); ECSqlStatement stmt_With_InvirtualSet; ECSqlStatement stmt_With_IdSet; @@ -74,11 +74,11 @@ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet_with IECSqlBinder& binder = stmt_With_IdSet.GetBinder(1); for(int i = 0;i<2000;i++) - { + { int randNum = i+1; ASSERT_EQ(ECSqlStatus::Success, binder.AddArrayElement().BindInt64(randNum)); emptyIdset.insert(BeInt64Id(randNum)); - } + } std::shared_ptr> idSetPtr = std::make_shared>(emptyIdset); ASSERT_EQ(ECSqlStatus::Success, stmt_With_InvirtualSet.BindVirtualSet(1, idSetPtr)); @@ -87,11 +87,11 @@ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet_with int i = 0; StopWatch timer_InVirtual_Set(true); while(stmt_With_InvirtualSet.Step() == BE_SQLITE_ROW) - { + { ASSERT_EQ((*iterator_set).GetValue(), stmt_With_InvirtualSet.GetValueInt64(0)); ++iterator_set; i++; - } + } timer_InVirtual_Set.Stop(); LOGTODB(TEST_DETAILS, timer_InVirtual_Set.GetElapsedSeconds(), i, SqlPrintfString("Statement: \"%s\"", stmt_With_InvirtualSet.GetECSql())); @@ -99,20 +99,20 @@ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet_with i = 0; StopWatch timer_IdSet(true); while(stmt_With_IdSet.Step() == BE_SQLITE_ROW) - { + { ASSERT_EQ((*iterator_set).GetValue(), stmt_With_IdSet.GetValueInt64(0)); ++iterator_set; i++; - } + } timer_IdSet.Stop(); LOGTODB(TEST_DETAILS, timer_IdSet.GetElapsedSeconds(), i, SqlPrintfString("Statement: \"%s\"", stmt_With_IdSet.GetECSql())); - } + } //--------------------------------------------------------------------------------------- // @bsiclass //+---------------+---------------+---------------+---------------+---------------+------ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet_with_custom_schema) - { + { ASSERT_EQ(BentleyStatus::SUCCESS, SetupECDb("SimpleComparisonBetweenInVirtualSet_and_IdSet_with_custom_schema.ecdb", SchemaItem(R"xml( @@ -123,17 +123,17 @@ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet_with ECSqlStatement insert_stmt; ASSERT_EQ(ECSqlStatus::Success, insert_stmt.Prepare(m_ecdb, "insert into ts.Foo(UnIndexed_Prop) values(?)")); for(int i = 0;i<1000000;i++) - { + { insert_stmt.ClearBindings(); insert_stmt.Reset(); int rand = (i+1) % 70000; insert_stmt.BindInt64(1, rand); ASSERT_EQ(BE_SQLITE_DONE, insert_stmt.Step()); - } + } std::vector v = {"ECInstanceId", "UnIndexed_Prop"}; for(auto& str: v) - { + { ECSqlStatement stmt_With_InvirtualSet; ECSqlStatement stmt_With_IdSet; IdSet emptyIdset; @@ -143,11 +143,11 @@ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet_with IECSqlBinder& binder = stmt_With_IdSet.GetBinder(1); for(int i = 0;i<2000;i++) - { + { int randNum = i+1; ASSERT_EQ(ECSqlStatus::Success, binder.AddArrayElement().BindInt64(randNum)); emptyIdset.insert(BeInt64Id(randNum)); - } + } std::shared_ptr> idSetPtr = std::make_shared>(emptyIdset); ASSERT_EQ(ECSqlStatus::Success, stmt_With_InvirtualSet.BindVirtualSet(1, idSetPtr)); @@ -156,11 +156,11 @@ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet_with int i = 0; StopWatch timer_InVirtual_Set(true); while(stmt_With_InvirtualSet.Step() == BE_SQLITE_ROW) - { + { ASSERT_EQ((*iterator_set).GetValue(), stmt_With_InvirtualSet.GetValueInt64(0)); ++iterator_set; i++; - } + } timer_InVirtual_Set.Stop(); LOGTODB(TEST_DETAILS, timer_InVirtual_Set.GetElapsedSeconds(), i, SqlPrintfString("Statement: \"%s\"", stmt_With_InvirtualSet.GetECSql())); @@ -168,21 +168,21 @@ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet_with i = 0; StopWatch timer_IdSet(true); while(stmt_With_IdSet.Step() == BE_SQLITE_ROW) - { + { ASSERT_EQ((*iterator_set).GetValue(), stmt_With_IdSet.GetValueInt64(0)); ++iterator_set; i++; - } + } timer_IdSet.Stop(); LOGTODB(TEST_DETAILS, timer_IdSet.GetElapsedSeconds(), i, SqlPrintfString("Statement: \"%s\"", stmt_With_IdSet.GetECSql())); + } } - } //--------------------------------------------------------------------------------------- // @bsiclass //+---------------+---------------+---------------+---------------+---------------+------ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenJoinedQuery_and_QueryWithWhereClause_with_IdSet) - { + { ASSERT_EQ(BentleyStatus::SUCCESS, SetupECDb("SimpleComparisonBetweenJoinedQuery_and_QueryWithWhereClause_with_IdSet.ecdb", SchemaItem(R"xml( @@ -193,17 +193,17 @@ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenJoinedQuery_and_QueryWithWh ECSqlStatement insert_stmt; ASSERT_EQ(ECSqlStatus::Success, insert_stmt.Prepare(m_ecdb, "insert into ts.Foo(UnIndexed_Prop) values(?)")); for(int i = 0;i<1000000;i++) - { + { insert_stmt.ClearBindings(); insert_stmt.Reset(); int rand = (i+1) % 70000; insert_stmt.BindInt64(1, rand); ASSERT_EQ(BE_SQLITE_DONE, insert_stmt.Step()); - } + } std::vector v = {"ECInstanceId", "UnIndexed_Prop"}; for(auto& str: v) - { + { ECSqlStatement stmt_With_Join; ECSqlStatement stmt_With_WhereClause; IdSet emptyIdset; @@ -213,28 +213,28 @@ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenJoinedQuery_and_QueryWithWh IECSqlBinder& binderForJoin = stmt_With_Join.GetBinder(1); for(int i = 0;i<2000;i++) - { + { int randNum = i+1; ASSERT_EQ(ECSqlStatus::Success, binderForJoin.AddArrayElement().BindInt64(randNum)); emptyIdset.insert(BeInt64Id(randNum)); - } + } IECSqlBinder& binderForWhereClause = stmt_With_WhereClause.GetBinder(1); for(int i = 0;i<2000;i++) - { + { int randNum = i+1; ASSERT_EQ(ECSqlStatus::Success, binderForWhereClause.AddArrayElement().BindInt64(randNum)); - } + } auto iterator_set = emptyIdset.begin(); int i = 0; StopWatch timer_Join_Query(true); while(stmt_With_Join.Step() == BE_SQLITE_ROW) - { + { ASSERT_EQ((*iterator_set).GetValue(), stmt_With_Join.GetValueInt64(0)); ++iterator_set; i++; - } + } timer_Join_Query.Stop(); LOGTODB(TEST_DETAILS, timer_Join_Query.GetElapsedSeconds(), i, SqlPrintfString("Statement: \"%s\"", stmt_With_Join.GetECSql())); @@ -242,13 +242,13 @@ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenJoinedQuery_and_QueryWithWh i = 0; StopWatch timer_Query_With_WhereClause(true); while(stmt_With_WhereClause.Step() == BE_SQLITE_ROW) - { + { ASSERT_EQ((*iterator_set).GetValue(), stmt_With_WhereClause.GetValueInt64(0)); ++iterator_set; i++; - } + } timer_Query_With_WhereClause.Stop(); LOGTODB(TEST_DETAILS, timer_Query_With_WhereClause.GetElapsedSeconds(), i, SqlPrintfString("Statement: \"%s\"", stmt_With_WhereClause.GetECSql())); + } } - } END_ECDBUNITTESTS_NAMESPACE \ No newline at end of file diff --git a/iModelJsNodeAddon/api_package/ts/package-lock.json b/iModelJsNodeAddon/api_package/ts/package-lock.json index ad4df60ebe9..67bb28d285c 100644 --- a/iModelJsNodeAddon/api_package/ts/package-lock.json +++ b/iModelJsNodeAddon/api_package/ts/package-lock.json @@ -863,9 +863,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.17.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.10.tgz", - "integrity": "sha512-/jrvh5h6NXhEauFFexRin69nA0uHJ5gwk4iDivp/DeoEua3uwCUto6PC86IpRITBOs4+6i2I56K5x5b6WYGXHA==", + "version": "20.17.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.14.tgz", + "integrity": "sha512-w6qdYetNL5KRBiSClK/KWai+2IMEJuAj+EujKCumalFOwXtvOXaEan9AuwcRID2IcOIAWSIfR495hBtgKlx2zg==", "dev": true, "license": "MIT", "dependencies": { From 7863982f43832790d3bba51e9551eb325dbc11e9 Mon Sep 17 00:00:00 2001 From: soham-bentley <177123878+soham-bentley@users.noreply.github.com> Date: Wed, 22 Jan 2025 19:45:20 +0530 Subject: [PATCH 38/43] Update in logic to get statement state --- iModelCore/BeSQLite/BeSQLite.cpp | 7 ++++--- .../BeSQLite/PublicAPI/BeSQLite/BeSQLite.h | 16 ++++++++-------- .../ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp | 3 ++- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/iModelCore/BeSQLite/BeSQLite.cpp b/iModelCore/BeSQLite/BeSQLite.cpp index 6e3ee927575..e6477ecdcb7 100644 --- a/iModelCore/BeSQLite/BeSQLite.cpp +++ b/iModelCore/BeSQLite/BeSQLite.cpp @@ -1434,11 +1434,12 @@ void Statement::DumpResults() /*--------------------------------------------------------------------------------------- * @bsimethod +---------------+---------------+---------------+---------------+---------------+------*/ -StatementState Statement::GetStatementState() +bool Statement::TryGetStatementState(StatementState& state) { if(!IsPrepared()) - return StatementState::NOT_PREPARED; - return (StatementState)getStatementState(m_stmt); + return false; + state = (StatementState)getStatementState(m_stmt); + return true; } /*---------------------------------------------------------------------------------**//** diff --git a/iModelCore/BeSQLite/PublicAPI/BeSQLite/BeSQLite.h b/iModelCore/BeSQLite/PublicAPI/BeSQLite/BeSQLite.h index 489339144a1..dd3fe73a75d 100644 --- a/iModelCore/BeSQLite/PublicAPI/BeSQLite/BeSQLite.h +++ b/iModelCore/BeSQLite/PublicAPI/BeSQLite/BeSQLite.h @@ -437,14 +437,13 @@ ENUM_IS_FLAGS(DbDeserializeOptions) //======================================================================================= // @bsiclass //======================================================================================= -enum StatementState +enum class StatementState { // The first four values are in accordance with sqlite3.c - VDBE_INIT_STATE = 0, //!< Prepared statement under construction - VDBE_READY_STATE = 1, //!< Ready to run but not yet started - VDBE_RUN_STATE = 2, //!< Run in progress - VDBE_HALT_STATE = 3, //!< Finished. Need reset() or finalize() - NOT_PREPARED = 4, //!< Statement not yet prepared + Init = 0, //!< Prepared statement under construction + Ready = 1, //!< Ready to run but not yet started + Run = 2, //!< Run in progress + Halt = 3, //!< Finished. Need reset() or finalize() }; //======================================================================================= @@ -998,8 +997,9 @@ struct Statement : NonCopyableClass //! Dump query results to stdout, for debugging purposes BE_SQLITE_EXPORT void DumpResults(); - //! Gets the state in which a particular statement is - BE_SQLITE_EXPORT StatementState GetStatementState(); + //! Tries to get the state in which a particular statement is. Returns true if it is successful in getting the state of the statement otherwise returns false + //! If it returns true, the state value is stored in the passed reference argument. + BE_SQLITE_EXPORT bool TryGetStatementState(StatementState&); SqlStatementP GetSqlStatementP() const {return m_stmt;} // for direct use of sqlite3 api operator SqlStatementP(){return m_stmt;} // for direct use of sqlite3 api diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp index 4bad0c27320..6ed10061894 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparedStatement.cpp @@ -211,7 +211,8 @@ DbResult SingleECSqlPreparedStatement::DoStep() if (SUCCESS != AssertIsValid()) return BE_SQLITE_ERROR; - if(m_sqliteStatement.GetStatementState() == StatementState::VDBE_READY_STATE) + StatementState state; + if(m_sqliteStatement.TryGetStatementState(state) && state == StatementState::Ready) { if (!m_parameterMap.OnBeforeFirstStep().IsSuccess()) return BE_SQLITE_ERROR; From 00060aae60badb9a583baeaf9ecb5f6884ff73c7 Mon Sep 17 00:00:00 2001 From: soham-bentley <177123878+soham-bentley@users.noreply.github.com> Date: Wed, 22 Jan 2025 20:08:14 +0530 Subject: [PATCH 39/43] Changelog updated --- iModelCore/ECDb/CHANGES.md | 13 ++++++++++++- iModelCore/ECDb/PublicAPI/ECDb/ECDb.h | 2 +- iModelCore/ECDb/Tests/NonPublished/ECDbTests.cpp | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/iModelCore/ECDb/CHANGES.md b/iModelCore/ECDb/CHANGES.md index 390f951a028..728614b902b 100644 --- a/iModelCore/ECDb/CHANGES.md +++ b/iModelCore/ECDb/CHANGES.md @@ -5,7 +5,18 @@ This document including important changes to syntax or file format. | Module | Version | | ------- | --------- | | Profile | `4.0.0.5` | -| ECSQL | `2.0.0.0` | +| ECSQL | `2.0.1.1` | + +## ## `01/22/2025`: Added IdSet Virtual Table in ECSQL +* ECSql version change `2.0.1.0` -> `2.0.1.1`. +* Added IdSet Virtual Table in ECSQL +* Example: `Select test.str_prop, test.int_prop, v.id from ts.A test RIGHT OUTER JOIN ECVLib.IdSet(:idSet_param) v on test.ECInstanceId = v.id`. + +## ## `01/10/2025`: Added support for CTE subquery with alias +* ECSql version change `2.0.0.0` -> `2.0.1.0`. +* Added support for CTE subquery with alias +* Example: `select a.x from (with tmp(x) as (SELECT e.i FROM aps.TestElement e order by e.i LIMIT 1) select x from tmp) a`. + ## ## `09/05/2024`: Remove class names ALIAS support and Disqualify_polymorphic_constraint(+) support in UPDATE & DELETE statements * ECSql version change `1.2.14.0` -> `2.0.0.0`. diff --git a/iModelCore/ECDb/PublicAPI/ECDb/ECDb.h b/iModelCore/ECDb/PublicAPI/ECDb/ECDb.h index a02483c635c..ccdf1fb4547 100644 --- a/iModelCore/ECDb/PublicAPI/ECDb/ECDb.h +++ b/iModelCore/ECDb/PublicAPI/ECDb/ECDb.h @@ -303,7 +303,7 @@ struct EXPORT_VTABLE_ATTRIBUTE ECDb : Db // e.g. Remove a sql function or change required argument or format of its return value. // Sub1: Backward compatible change to 'Syntax'. For example adding new syntax/functions but not breaking any existing. // Sub2: Backward compatible change to 'Runtime'. For example adding a new sql function. - static BeVersion GetECSqlVersion() { return BeVersion(2, 0, 0, 0); } + static BeVersion GetECSqlVersion() { return BeVersion(2, 0, 1, 1); } //! Gets the current version of the ECDb profile static ProfileVersion CurrentECDbProfileVersion() { return ProfileVersion(4, 0, 0, 5); } diff --git a/iModelCore/ECDb/Tests/NonPublished/ECDbTests.cpp b/iModelCore/ECDb/Tests/NonPublished/ECDbTests.cpp index 88b25c8b251..1a8f0edecf2 100644 --- a/iModelCore/ECDb/Tests/NonPublished/ECDbTests.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/ECDbTests.cpp @@ -689,7 +689,7 @@ TEST_F(ECDbTestFixture, GetAndChangeGUIDForDb) //+---------------+---------------+---------------+---------------+---------------+------ TEST_F(ECDbTestFixture, CurrentECSqlVersion) { - BeVersion expectedVersion (2, 0, 0, 0); + BeVersion expectedVersion (2, 0, 1, 1); ASSERT_EQ(ECDb::GetECSqlVersion(), expectedVersion); } From ebf17132bd33db2efa5301fdcb7f055809441705 Mon Sep 17 00:00:00 2001 From: soham-bentley <177123878+soham-bentley@users.noreply.github.com> Date: Thu, 23 Jan 2025 15:19:18 +0530 Subject: [PATCH 40/43] comments updated --- iModelCore/BeSQLite/PublicAPI/BeSQLite/BeSQLite.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iModelCore/BeSQLite/PublicAPI/BeSQLite/BeSQLite.h b/iModelCore/BeSQLite/PublicAPI/BeSQLite/BeSQLite.h index dd3fe73a75d..f52d9d85fa9 100644 --- a/iModelCore/BeSQLite/PublicAPI/BeSQLite/BeSQLite.h +++ b/iModelCore/BeSQLite/PublicAPI/BeSQLite/BeSQLite.h @@ -998,7 +998,7 @@ struct Statement : NonCopyableClass BE_SQLITE_EXPORT void DumpResults(); //! Tries to get the state in which a particular statement is. Returns true if it is successful in getting the state of the statement otherwise returns false - //! If it returns true, the state value is stored in the passed reference argument. + //! If the returned value is true, the state value is stored in the passed reference argument. BE_SQLITE_EXPORT bool TryGetStatementState(StatementState&); SqlStatementP GetSqlStatementP() const {return m_stmt;} // for direct use of sqlite3 api From 4fb0f75358dad82539455423f0d1d97e58ca1cc2 Mon Sep 17 00:00:00 2001 From: soham-bentley <177123878+soham-bentley@users.noreply.github.com> Date: Fri, 24 Jan 2025 13:34:52 +0530 Subject: [PATCH 41/43] Using EqualsI instead of EqualsIAscii --- iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp b/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp index 09ea0834210..4668106ccf5 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ValueExp.cpp @@ -817,7 +817,7 @@ Exp::FinalizeParseStatus MemberFunctionCallExp::_FinalizeParsing(ECSqlParseConte if (mode == Exp::FinalizeParseMode::AfterFinalizingChildren) { if (this->IsTableValuedFunc()) { - if(m_functionName.EqualsIAscii(IdSetModule::NAME)) + if(m_functionName.EqualsI(IdSetModule::NAME)) { ValueExp const* argExp = GetArgument(0); if(argExp == nullptr) @@ -960,7 +960,7 @@ Utf8String MemberFunctionCallExp::_ToString() const //+---------------+---------------+---------------+---------------+---------------+------ bool MemberFunctionCallExp::_TryDetermineParameterExpType(ECSqlParseContext& ctx, ParameterExp& parameterExp) const { - if(this->IsTableValuedFunc() && m_functionName.EqualsIAscii(IdSetModule::NAME)) + if(this->IsTableValuedFunc() && m_functionName.EqualsI(IdSetModule::NAME)) { parameterExp.SetTargetExpInfo(ECSqlTypeInfo::CreatePrimitive(ECN::PRIMITIVETYPE_Long, true, EXTENDEDTYPENAME_Id)); return true; From ab181c71cca339ed9d6d1db0431b3799b93c4782 Mon Sep 17 00:00:00 2001 From: soham-bentley <177123878+soham-bentley@users.noreply.github.com> Date: Fri, 24 Jan 2025 17:37:58 +0530 Subject: [PATCH 42/43] Comments resolved and tests added --- iModelCore/ECDb/ECDb/BuiltInVTabs.cpp | 10 +- .../ECSql/PragmaECSqlPreparedStatement.cpp | 3 +- .../ECDbIdSetVirtualTableTests.cpp | 135 ++++++++++++++++++ 3 files changed, 141 insertions(+), 7 deletions(-) diff --git a/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp b/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp index f3f9d9c28c5..595976c99c7 100644 --- a/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp +++ b/iModelCore/ECDb/ECDb/BuiltInVTabs.cpp @@ -189,15 +189,13 @@ DbResult IdSetModule::IdSetTable::IdSetCursor::FilterJSONStringIntoArray(BeJsDoc GetTable().SetError("IdSet vtab: The argument should be a valid JSON array of ids"); return BE_SQLITE_ERROR; } - bool flag = doc.ForEachArrayMember([&](BeJsValue::ArrayIndex a, BeJsConst k1) - { - if(BE_SQLITE_OK != FilterJSONBasedOnType(k1)) - { + bool flag = doc.ForEachArrayMember([&](BeJsValue::ArrayIndex a, BeJsConst k1) { + if(BE_SQLITE_OK != FilterJSONBasedOnType(k1)) { GetTable().SetError(SqlPrintfString("IdSet vtab: The element with index %u is invalid", a)); return true; - } + } return false; - }); + }); if(flag) return BE_SQLITE_ERROR; return BE_SQLITE_OK; diff --git a/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.cpp b/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.cpp index 57f9e4e51f9..72975c58387 100644 --- a/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/PragmaECSqlPreparedStatement.cpp @@ -643,8 +643,9 @@ DbResult PragmaECSqlPreparedStatement::DoStep() { } DbResult res = m_resultSet->Step(); + // if step actually succeeded and returned BE_SQLITE_DONE or BE_SQLITE_ROW on the sqlite side then we set this flag to false if flag is true, if the returned value is something else like BE_SQLITE_SCHEMA or anything else we don't set the flag to false if((res == BE_SQLITE_DONE || res == BE_SQLITE_ROW) && m_isFirstStep) - m_isFirstStep = false; // if step actually successded on the sqlite side then we set this flag to false if flag is true + m_isFirstStep = false; return res; } diff --git a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp index de85b00aabc..c5c9f2b82b9 100644 --- a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp @@ -805,5 +805,140 @@ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetWithJOINS) { } } +//--------------------------------------------------------------------------------------- +// @bsimethod +//+---------------+---------------+---------------+---------------+---------------+------ +TEST_F(ECDbIdSetVirtualTableTestFixture, Testing_casing_of_IdSet) { + ASSERT_EQ(BentleyStatus::SUCCESS, SetupECDb("IdSet_with_JOINS.ecdb", SchemaItem(R"xml( + + + + + + )xml"))); + std::vector listOfIds; + std::vector listOfStringVal = {"str1", "str2", "str3", "str4","str5", "str6", "str7", "str8", "str9", "str10"}; + ECSqlStatement insertStmt; + ASSERT_EQ(ECSqlStatus::Success, insertStmt.Prepare(m_ecdb, "INSERT INTO ts.A(str_prop, int_prop) VALUES(?,?)")); + for(int i =1;i<=10;i++) + { + insertStmt.BindText(1, listOfStringVal[i-1], IECSqlBinder::MakeCopy::No); + insertStmt.BindInt(2, i); + ECInstanceKey key; + if(insertStmt.Step(key) != BE_SQLITE_DONE) + break; + listOfIds.push_back((BeInt64Id)key.GetInstanceId()); + insertStmt.ClearBindings(); + insertStmt.Reset(); + } + ASSERT_EQ(10, listOfIds.size()); + if("testing normal joins with IdSet") + { + ECSqlStatement selectStmt; + ASSERT_EQ(ECSqlStatus::Success, selectStmt.Prepare(m_ecdb, "Select test.str_prop, test.int_prop, v.id from ts.A test JOIN ECVLib.idset(?) v on test.ECInstanceId = v.id")); + IECSqlBinder& binder = selectStmt.GetBinder(1); + ASSERT_EQ(ECSqlStatus::Success,binder.AddArrayElement().BindId(listOfIds[3])); + ASSERT_EQ(ECSqlStatus::Success,binder.AddArrayElement().BindId(listOfIds[6])); + ASSERT_EQ(ECSqlStatus::Success,binder.AddArrayElement().BindId(listOfIds[7])); + ASSERT_STREQ("str_prop", selectStmt.GetColumnInfo(0).GetProperty()->GetName().c_str()); + ASSERT_STREQ("int_prop", selectStmt.GetColumnInfo(1).GetProperty()->GetName().c_str()); + ASSERT_STREQ("id", selectStmt.GetColumnInfo(2).GetProperty()->GetName().c_str()); + ASSERT_EQ(BE_SQLITE_ROW, selectStmt.Step()); + ASSERT_STREQ("str4", selectStmt.GetValueText(0)) << "str_prop"; + ASSERT_EQ(4, selectStmt.GetValueInt(1)) << "int_prop"; + ASSERT_EQ(listOfIds[3], selectStmt.GetValueId(2)) << "id"; + + ASSERT_EQ(BE_SQLITE_ROW, selectStmt.Step()); + ASSERT_STREQ("str7", selectStmt.GetValueText(0)) << "str_prop"; + ASSERT_EQ(7, selectStmt.GetValueInt(1)) << "int_prop"; + ASSERT_EQ(listOfIds[6], selectStmt.GetValueId(2)) << "id"; + + ASSERT_EQ(BE_SQLITE_ROW, selectStmt.Step()); + ASSERT_STREQ("str8", selectStmt.GetValueText(0)) << "str_prop"; + ASSERT_EQ(8, selectStmt.GetValueInt(1)) << "int_prop"; + ASSERT_EQ(listOfIds[7], selectStmt.GetValueId(2)) << "id"; + + ASSERT_EQ(BE_SQLITE_DONE, selectStmt.Step()); + + ASSERT_STREQ("SELECT [test].[str_prop],[test].[int_prop],v.id FROM (SELECT [Id] ECInstanceId,88 ECClassId,[str_prop],[int_prop] FROM [main].[ts_A]) [test] INNER JOIN idset(:_ecdb_sqlparam_ix1_col1) v ON [test].[ECInstanceId]=v.id ", selectStmt.GetNativeSql()); + } + if("testing right outer join with IdSet") + { + ECSqlStatement selectStmt; + ASSERT_EQ(ECSqlStatus::Success, selectStmt.Prepare(m_ecdb, "Select test.str_prop, test.int_prop, v.id from ts.A test RIGHT OUTER JOIN ECVLib.idSet(?) v on test.ECInstanceId = v.id")); + IECSqlBinder& binder = selectStmt.GetBinder(1); + ASSERT_EQ(ECSqlStatus::Success,binder.AddArrayElement().BindId(listOfIds[3])); + ASSERT_EQ(ECSqlStatus::Success,binder.AddArrayElement().BindId(listOfIds[6])); + ASSERT_EQ(ECSqlStatus::Success,binder.AddArrayElement().BindId(listOfIds[7])); + ASSERT_STREQ("str_prop", selectStmt.GetColumnInfo(0).GetProperty()->GetName().c_str()); + ASSERT_STREQ("int_prop", selectStmt.GetColumnInfo(1).GetProperty()->GetName().c_str()); + ASSERT_STREQ("id", selectStmt.GetColumnInfo(2).GetProperty()->GetName().c_str()); + ASSERT_EQ(BE_SQLITE_ROW, selectStmt.Step()); + ASSERT_STREQ("str4", selectStmt.GetValueText(0)) << "str_prop"; + ASSERT_EQ(4, selectStmt.GetValueInt(1)) << "int_prop"; + ASSERT_EQ(listOfIds[3], selectStmt.GetValueId(2)) << "id"; + + ASSERT_EQ(BE_SQLITE_ROW, selectStmt.Step()); + ASSERT_STREQ("str7", selectStmt.GetValueText(0)) << "str_prop"; + ASSERT_EQ(7, selectStmt.GetValueInt(1)) << "int_prop"; + ASSERT_EQ(listOfIds[6], selectStmt.GetValueId(2)) << "id"; + + ASSERT_EQ(BE_SQLITE_ROW, selectStmt.Step()); + ASSERT_STREQ("str8", selectStmt.GetValueText(0)) << "str_prop"; + ASSERT_EQ(8, selectStmt.GetValueInt(1)) << "int_prop"; + ASSERT_EQ(listOfIds[7], selectStmt.GetValueId(2)) << "id"; + + ASSERT_EQ(BE_SQLITE_DONE, selectStmt.Step()); + ASSERT_STREQ("SELECT [test].[str_prop],[test].[int_prop],v.id FROM (SELECT [Id] ECInstanceId,88 ECClassId,[str_prop],[int_prop] FROM [main].[ts_A]) [test] RIGHT OUTER JOIN idSet(:_ecdb_sqlparam_ix1_col1) v ON [test].[ECInstanceId]=v.id ", selectStmt.GetNativeSql()); + } + { + std::vector hexIds = std::vector{"0x1", "0x2", "0x3", "4", "5"}; + + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT ECInstanceId FROM ECVLib.IDset(?), meta.ECClassDef where ECInstanceId = id group by ECInstanceId")); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + for(int i =0;i Date: Mon, 27 Jan 2025 20:37:25 +0530 Subject: [PATCH 43/43] Made IdSet experimental feature --- iModelCore/ECDb/ECDb/ECSql/ECSqlPreparer.cpp | 7 ++ iModelCore/ECDb/ECDb/IssueReporter.cpp | 1 + iModelCore/ECDb/ECDb/IssueReporter.h | 1 + .../NonPublished/ConcurrentQueryTest_V2.cpp | 24 +++- .../ECDbIdSetVirtualTableTests.cpp | 112 +++++++++++++++++- .../Performance/PerformanceIdSetTests.cpp | 17 +++ 6 files changed, 160 insertions(+), 2 deletions(-) diff --git a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparer.cpp b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparer.cpp index c8e60af4b50..8f504fc37bf 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparer.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ECSqlPreparer.cpp @@ -826,6 +826,13 @@ ECSqlStatus ECSqlExpPreparer::PrepareClassRefExp(NativeSqlBuilder::List& nativeS //+---------------+---------------+---------------+---------------+---------------+------ //static ECSqlStatus ECSqlExpPreparer::PrepareTableValuedFunctionExp(NativeSqlBuilder::List& nativeSqlSnippets, ECSqlPrepareContext& ctx, TableValuedFunctionExp const& exp) { + + if (exp.GetFunctionExp()->GetFunctionName().EqualsI(IdSetModule::NAME) && !QueryOptionExperimentalFeaturesEnabled(ctx.GetECDb(), exp)) + { + ctx.Issues().ReportV(IssueSeverity::Error, IssueCategory::BusinessProperties, IssueType::ECSQL, ECDbIssueId::ECDb_0737, "'%s' virtual table is experimental feature and disabled by default.", IdSetModule::NAME); + return ECSqlStatus::InvalidECSql; + } + NativeSqlBuilder builder; builder.Append(exp.GetFunctionExp()->GetFunctionName()); builder.AppendParenLeft(); diff --git a/iModelCore/ECDb/ECDb/IssueReporter.cpp b/iModelCore/ECDb/ECDb/IssueReporter.cpp index 41edd97aa4d..214de74db06 100644 --- a/iModelCore/ECDb/ECDb/IssueReporter.cpp +++ b/iModelCore/ECDb/ECDb/IssueReporter.cpp @@ -733,6 +733,7 @@ IssueId ECDbIssueId::ECDb_0733 = IssueId("ECDb_0733"); IssueId ECDbIssueId::ECDb_0734 = IssueId("ECDb_0734"); IssueId ECDbIssueId::ECDb_0735 = IssueId("ECDb_0735"); IssueId ECDbIssueId::ECDb_0736 = IssueId("ECDb_0736"); +IssueId ECDbIssueId::ECDb_0737 = IssueId("ECDb_0737"); //--------------------------------------------------------------------------------------- // @bsimethod diff --git a/iModelCore/ECDb/ECDb/IssueReporter.h b/iModelCore/ECDb/ECDb/IssueReporter.h index cc49c14fef2..80fd1ef4430 100644 --- a/iModelCore/ECDb/ECDb/IssueReporter.h +++ b/iModelCore/ECDb/ECDb/IssueReporter.h @@ -754,6 +754,7 @@ struct ECDB_EXPORT ECDbIssueId static ECN::IssueId ECDb_0734; static ECN::IssueId ECDb_0735; static ECN::IssueId ECDb_0736; + static ECN::IssueId ECDb_0737; }; //--------------------------------------------------------------------------------------- diff --git a/iModelCore/ECDb/Tests/NonPublished/ConcurrentQueryTest_V2.cpp b/iModelCore/ECDb/Tests/NonPublished/ConcurrentQueryTest_V2.cpp index c41d41d7617..c93e41dc53f 100644 --- a/iModelCore/ECDb/Tests/NonPublished/ConcurrentQueryTest_V2.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/ConcurrentQueryTest_V2.cpp @@ -1153,6 +1153,7 @@ TEST_F(ConcurrentQueryFixture, CommentAtEndOfECSql) { TEST_F(ConcurrentQueryFixture, ReaderBindingForIdSetVirtualTable) { ASSERT_EQ(DbResult::BE_SQLITE_OK, SetupECDb("ConcurrentQuery_Simple.ecdb")); + ConcurrentQueryFixture::EnableECSqlExperimentalFeatures(m_ecdb, true); { auto& mgr = ConcurrentQueryMgr::GetInstance(m_ecdb); BeIdSet idSet; @@ -1208,7 +1209,28 @@ TEST_F(ConcurrentQueryFixture, ReaderBindingForIdSetVirtualTable) { } ASSERT_EQ(vsRowCount,0); } - + ConcurrentQueryMgr::Shutdown(m_ecdb); + ConcurrentQueryFixture::EnableECSqlExperimentalFeatures(m_ecdb, false); + { + auto& mgr = ConcurrentQueryMgr::GetInstance(m_ecdb); + BeIdSet idSet; + idSet.insert(BeInt64Id(10)); + idSet.insert(BeInt64Id(20)); + idSet.insert(BeInt64Id(30)); + idSet.insert(BeInt64Id(40)); + int vsRowCount = 0; + + ECSqlReader vsReaderIdSet(mgr, "select id from ECVLib.IdSet(?)", + ECSqlParams().BindIdSet(1, idSet)); + + try{ + vsReaderIdSet.Next(); + } + catch (std::runtime_error e){ + ASSERT_STREQ("'IdSet' virtual table is experimental feature and disabled by default.", e.what()); + ASSERT_EQ(vsRowCount,0); + } + } } END_ECDBUNITTESTS_NAMESPACE diff --git a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp index c5c9f2b82b9..3bfd225b192 100644 --- a/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/ECDbIdSetVirtualTableTests.cpp @@ -17,6 +17,8 @@ struct ECDbIdSetVirtualTableTestFixture : ECDbTestFixture {}; //+---------------+---------------+---------------+---------------+---------------+------ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetModuleTest) { ASSERT_EQ(BE_SQLITE_OK, SetupECDb("vtab.ecdb")); + ASSERT_FALSE(IsECSqlExperimentalFeaturesEnabled(m_ecdb)); + ASSERT_TRUE(EnableECSqlExperimentalFeatures(m_ecdb, true)); { ECSqlStatement stmt; ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet('[1,2,3,4,5]')")); @@ -616,6 +618,8 @@ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetModuleTest) { // "3.0" is not allowed in IdSet VT so should fail and log error ASSERT_EQ(BE_SQLITE_ERROR, stmt.Step()); } + ASSERT_TRUE(IsECSqlExperimentalFeaturesEnabled(m_ecdb)); + ASSERT_FALSE(EnableECSqlExperimentalFeatures(m_ecdb, false)); } //--------------------------------------------------------------------------------------- @@ -629,6 +633,9 @@ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetWithJOINS) { )xml"))); + + ASSERT_FALSE(IsECSqlExperimentalFeaturesEnabled(m_ecdb)); + ASSERT_TRUE(EnableECSqlExperimentalFeatures(m_ecdb, true)); std::vector listOfIds; std::vector listOfStringVal = {"str1", "str2", "str3", "str4","str5", "str6", "str7", "str8", "str9", "str10"}; ECSqlStatement insertStmt; @@ -803,7 +810,106 @@ TEST_F(ECDbIdSetVirtualTableTestFixture, IdSetWithJOINS) { ECSqlStatement selectStmt; ASSERT_EQ(ECSqlStatus::InvalidECSql, selectStmt.Prepare(m_ecdb, "Select test.str_prop, test.int_prop, v.id from ts.A test OUTER JOIN ECVLib.IdSet(?) v on test.ECInstanceId = v.id")); } + ASSERT_TRUE(IsECSqlExperimentalFeaturesEnabled(m_ecdb)); + ASSERT_FALSE(EnableECSqlExperimentalFeatures(m_ecdb, false)); } +//--------------------------------------------------------------------------------------- +// @bsimethod +//+---------------+---------------+---------------+---------------+---------------+------ +TEST_F(ECDbIdSetVirtualTableTestFixture, experimental_test_feature) { + ASSERT_EQ(BE_SQLITE_OK, SetupECDb("experimental_test_feature.ecdb")); + { + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT id FROM ECVLib.IdSet('[1,2,3,4,5]') ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES")); + + int i = 0; + while (stmt.Step() == BE_SQLITE_ROW) + { + ASSERT_EQ((1+i++), stmt.GetValueInt64(0)); + } + ASSERT_EQ(i, 5); + } + { + std::vector hexIds = std::vector{"0x1", "0x2", "0x3", "4", "5"}; + + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT ECInstanceId FROM ECVLib.IdSet(?), meta.ECClassDef where ECInstanceId = id group by ECInstanceId OPTIONS ENABLE_EXPERIMENTAL_FEATURES = true")); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + for(int i =0;i )xml"))); + + ASSERT_FALSE(IsECSqlExperimentalFeaturesEnabled(m_ecdb)); + ASSERT_TRUE(EnableECSqlExperimentalFeatures(m_ecdb, true)); std::vector listOfIds; std::vector listOfStringVal = {"str1", "str2", "str3", "str4","str5", "str6", "str7", "str8", "str9", "str10"}; ECSqlStatement insertStmt; @@ -937,7 +1046,8 @@ TEST_F(ECDbIdSetVirtualTableTestFixture, Testing_casing_of_IdSet) { } ASSERT_EQ(i, 1); } - + ASSERT_TRUE(IsECSqlExperimentalFeaturesEnabled(m_ecdb)); + ASSERT_FALSE(EnableECSqlExperimentalFeatures(m_ecdb, false)); } diff --git a/iModelCore/ECDb/Tests/Performance/PerformanceIdSetTests.cpp b/iModelCore/ECDb/Tests/Performance/PerformanceIdSetTests.cpp index 63f3cd2143d..d6e3370f73a 100644 --- a/iModelCore/ECDb/Tests/Performance/PerformanceIdSetTests.cpp +++ b/iModelCore/ECDb/Tests/Performance/PerformanceIdSetTests.cpp @@ -15,6 +15,8 @@ struct PerformanceIdSetTests : ECDbTestFixture {}; TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet) { ASSERT_EQ(BE_SQLITE_OK, SetupECDb("SimpleComparisonBetweenInVirtualSet_and_IdSet.ecdb")); + ASSERT_FALSE(IsECSqlExperimentalFeaturesEnabled(m_ecdb)); + ASSERT_TRUE(EnableECSqlExperimentalFeatures(m_ecdb, true)); ECSqlStatement stmt_With_InvirtualSet; ECSqlStatement stmt_With_IdSet; std::vector v = {"0x373","0x38F", "0x32", "0x01"}; @@ -57,6 +59,8 @@ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet) } timer_IdSet.Stop(); LOGTODB(TEST_DETAILS, timer_IdSet.GetElapsedSeconds(), i, SqlPrintfString("Statement: \"%s\"", stmt_With_IdSet.GetECSql())); + ASSERT_TRUE(IsECSqlExperimentalFeaturesEnabled(m_ecdb)); + ASSERT_FALSE(EnableECSqlExperimentalFeatures(m_ecdb, false)); } //--------------------------------------------------------------------------------------- @@ -65,6 +69,8 @@ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet) TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet_with_large_set) { ASSERT_EQ(BE_SQLITE_OK, SetupECDb("SimpleComparisonBetweenInVirtualSet_and_IdSet_with_large_set.ecdb")); + ASSERT_FALSE(IsECSqlExperimentalFeaturesEnabled(m_ecdb)); + ASSERT_TRUE(EnableECSqlExperimentalFeatures(m_ecdb, true)); ECSqlStatement stmt_With_InvirtualSet; ECSqlStatement stmt_With_IdSet; IdSet emptyIdset; @@ -106,6 +112,8 @@ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet_with } timer_IdSet.Stop(); LOGTODB(TEST_DETAILS, timer_IdSet.GetElapsedSeconds(), i, SqlPrintfString("Statement: \"%s\"", stmt_With_IdSet.GetECSql())); + ASSERT_TRUE(IsECSqlExperimentalFeaturesEnabled(m_ecdb)); + ASSERT_FALSE(EnableECSqlExperimentalFeatures(m_ecdb, false)); } //--------------------------------------------------------------------------------------- @@ -120,6 +128,8 @@ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet_with )xml"))); + ASSERT_FALSE(IsECSqlExperimentalFeaturesEnabled(m_ecdb)); + ASSERT_TRUE(EnableECSqlExperimentalFeatures(m_ecdb, true)); ECSqlStatement insert_stmt; ASSERT_EQ(ECSqlStatus::Success, insert_stmt.Prepare(m_ecdb, "insert into ts.Foo(UnIndexed_Prop) values(?)")); for(int i = 0;i<1000000;i++) @@ -176,6 +186,8 @@ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenInVirtualSet_and_IdSet_with timer_IdSet.Stop(); LOGTODB(TEST_DETAILS, timer_IdSet.GetElapsedSeconds(), i, SqlPrintfString("Statement: \"%s\"", stmt_With_IdSet.GetECSql())); } + ASSERT_TRUE(IsECSqlExperimentalFeaturesEnabled(m_ecdb)); + ASSERT_FALSE(EnableECSqlExperimentalFeatures(m_ecdb, false)); } //--------------------------------------------------------------------------------------- @@ -190,6 +202,9 @@ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenJoinedQuery_and_QueryWithWh )xml"))); + ASSERT_FALSE(IsECSqlExperimentalFeaturesEnabled(m_ecdb)); + ASSERT_TRUE(EnableECSqlExperimentalFeatures(m_ecdb, true)); + ECSqlStatement insert_stmt; ASSERT_EQ(ECSqlStatus::Success, insert_stmt.Prepare(m_ecdb, "insert into ts.Foo(UnIndexed_Prop) values(?)")); for(int i = 0;i<1000000;i++) @@ -250,5 +265,7 @@ TEST_F(PerformanceIdSetTests, SimpleComparisonBetweenJoinedQuery_and_QueryWithWh timer_Query_With_WhereClause.Stop(); LOGTODB(TEST_DETAILS, timer_Query_With_WhereClause.GetElapsedSeconds(), i, SqlPrintfString("Statement: \"%s\"", stmt_With_WhereClause.GetECSql())); } + ASSERT_TRUE(IsECSqlExperimentalFeaturesEnabled(m_ecdb)); + ASSERT_FALSE(EnableECSqlExperimentalFeatures(m_ecdb, false)); } END_ECDBUNITTESTS_NAMESPACE \ No newline at end of file