Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added faster alternative to InVirtualSet() #874

Merged
merged 60 commits into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from 48 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
c90557b
commit for new added test
soham-bentley Sep 18, 2024
f919765
commit 2
soham-bentley Sep 20, 2024
31847a4
update
soham-bentley Sep 24, 2024
6ba6b4c
Fixed all the issues related to filtering of data in Virtual Table
soham-bentley Sep 25, 2024
c96e44b
Binder Type enhancement
soham-bentley Sep 25, 2024
7fab42d
trying to fix BestIndex
soham-bentley Sep 27, 2024
898b779
Fixed the single column issue in VT
soham-bentley Sep 30, 2024
9cd676e
Performance Tests Added
soham-bentley Sep 30, 2024
0ead317
Merge remote-tracking branch 'origin/main' into Soham/Virtual_Table
soham-bentley Sep 30, 2024
a1e8080
ValueExp_Issue_Id_Change due to conflicts while merging with main
soham-bentley Sep 30, 2024
83ef505
Added flag for binder info to check whether the binder is for a param…
soham-bentley Oct 3, 2024
66ea5c3
Tests Added
soham-bentley Oct 3, 2024
0bc2fb1
Crash for following expression stopped by providing null checks
soham-bentley Oct 3, 2024
cea7858
Merge branch 'main' into Soham/Virtual_Table
khanaffan Oct 3, 2024
c9da214
Kepping concurrentQueryImpl as close to as it was with minimal changes
soham-bentley Oct 4, 2024
386b11e
Merge remote-tracking branch 'origin/main' into Soham/Virtual_Table
soham-bentley Oct 4, 2024
da37f1e
Merge remote-tracking branch 'origin/Soham/Virtual_Table' into Soham/…
soham-bentley Oct 4, 2024
cfc8683
schema name changed to ECVLib and also file name changed
soham-bentley Oct 4, 2024
a7c2c6b
cleanup
soham-bentley Oct 4, 2024
b73ccf6
more cleanup
soham-bentley Oct 4, 2024
f484fe9
Performanvce Tests Updated
soham-bentley Oct 4, 2024
855013c
some comments resolved
soham-bentley Oct 4, 2024
c676b3a
Comments regarding constant name of IdSet table resolved
soham-bentley Oct 4, 2024
a04462c
binderInfo refactoring
soham-bentley Oct 5, 2024
59b1662
added flag to call _onbeforefirststep() once in PragmaECSQLStatement …
soham-bentley Oct 5, 2024
69d337c
changes as per suggestions by Affan
soham-bentley Oct 5, 2024
cc7dd6c
Performance test updated
soham-bentley Oct 7, 2024
7e7c044
Tests updated to prevent failure in pipeline
soham-bentley Oct 7, 2024
5c159b4
tests updated
soham-bentley Oct 7, 2024
70d7121
update in logic in IModelJsNative.cpp and concurrentquery
soham-bentley Oct 7, 2024
9d4d08d
performance tests indentation updated
soham-bentley Oct 7, 2024
3cc80e6
final update
soham-bentley Oct 8, 2024
73c07d4
OnBeforeFirstStep() logic updated by using m_isFirstStep flag
soham-bentley Oct 8, 2024
bc46d55
Merge remote-tracking branch 'origin/main' into Soham/Virtual_Table
soham-bentley Oct 8, 2024
05c4108
Merge remote-tracking branch 'origin/main' into Soham/Virtual_Table
soham-bentley Oct 14, 2024
4b33a80
More Tests added
soham-bentley Oct 17, 2024
8ae15d0
Merge remote-tracking branch 'origin/main' into Soham/Virtual_Table
soham-bentley Oct 17, 2024
2df5e37
Merge remote-tracking branch 'origin/main' into Soham/Virtual_Table
soham-bentley Oct 18, 2024
0b95c05
Merge remote-tracking branch 'origin/main' into Soham/Virtual_Table
soham-bentley Oct 18, 2024
4891f05
Added flag checking to m_isFirstStep flag so that when actually flag …
soham-bentley Oct 21, 2024
9d348fa
Merge remote-tracking branch 'origin/main' into Soham/Virtual_Table
soham-bentley Oct 21, 2024
0755553
Merge remote-tracking branch 'origin/main' into Soham/Virtual_Table
soham-bentley Oct 24, 2024
8d1a2cc
removing m_isFirstStep and identifying first step using statement state
soham-bentley Oct 24, 2024
d3d17ae
Merge remote-tracking branch 'origin/main' into Soham/Virtual_Table
soham-bentley Oct 25, 2024
3c1ab06
Merge remote-tracking branch 'origin/main' into Soham/Virtual_Table
soham-bentley Jan 6, 2025
7a06777
Merge remote-tracking branch 'origin/main' into Soham/Virtual_Table
soham-bentley Jan 15, 2025
cde738e
Comment updated
soham-bentley Jan 16, 2025
4eb8b7a
Merge remote-tracking branch 'origin/main' into Soham/Virtual_Table
soham-bentley Jan 16, 2025
a7aa217
Fixed the issue with the query SELECT e.i FROM aps.TestElement e INNE…
soham-bentley Jan 17, 2025
e09dd7c
More tests added
soham-bentley Jan 20, 2025
9d37a1f
More Performance Tests added
soham-bentley Jan 20, 2025
df39db1
indentation issue solved
soham-bentley Jan 22, 2025
dc82b4a
Merge remote-tracking branch 'origin/main' into Soham/Virtual_Table
soham-bentley Jan 22, 2025
7863982
Update in logic to get statement state
soham-bentley Jan 22, 2025
00060aa
Changelog updated
soham-bentley Jan 22, 2025
ebf1713
comments updated
soham-bentley Jan 23, 2025
302d7f7
Merge remote-tracking branch 'origin/main' into Soham/Virtual_Table
soham-bentley Jan 23, 2025
4fb0f75
Using EqualsI instead of EqualsIAscii
soham-bentley Jan 24, 2025
ab181c7
Comments resolved and tests added
soham-bentley Jan 24, 2025
76a6c28
Made IdSet experimental feature
soham-bentley Jan 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions iModelCore/BeSQLite/BeSQLite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1429,6 +1430,16 @@ void Statement::DumpResults()

Reset();
}

/*---------------------------------------------------------------------------------------
* @bsimethod
+---------------+---------------+---------------+---------------+---------------+------*/
StatementState Statement::GetStatementState()
{
if(!IsPrepared())
return StatementState::NOT_PREPARED;
return (StatementState)getStatementState(m_stmt);
}

/*---------------------------------------------------------------------------------**//**
* @bsimethod
Expand Down
16 changes: 16 additions & 0 deletions iModelCore/BeSQLite/PublicAPI/BeSQLite/BeSQLite.h
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,19 @@ enum class DbDeserializeOptions {
};
ENUM_IS_FLAGS(DbDeserializeOptions)

//=======================================================================================
// @bsiclass
//=======================================================================================
enum StatementState
{
// The first four values are in accordance with sqlite3.c
ColinKerr marked this conversation as resolved.
Show resolved Hide resolved
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
//=======================================================================================
Expand Down Expand Up @@ -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
};
Expand Down
9 changes: 9 additions & 0 deletions iModelCore/BeSQLite/SQLite/bentley-sqlite.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
188 changes: 186 additions & 2 deletions iModelCore/ECDb/ECDb/BuiltInVTabs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,192 @@ 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 = (int64_t)(*m_index);
return BE_SQLITE_OK;
}

//---------------------------------------------------------------------------------------
// @bsimethod
//---------------------------------------------------------------------------------------
DbResult IdSetModule::IdSetTable::IdSetCursor::GetColumn(int i, Context& ctx) {
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;
}

//---------------------------------------------------------------------------------------
// @bsimethod
//---------------------------------------------------------------------------------------
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 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;
}


//---------------------------------------------------------------------------------------
// @bsimethod
//---------------------------------------------------------------------------------------
DbResult IdSetModule::IdSetTable::IdSetCursor::FilterJSONBasedOnType(BeJsConst& val) {
if(val.isNull())
{
return BE_SQLITE_ERROR;
}
else if(val.isNumeric())
{
uint64_t id = val.GetUInt64();
if(id == 0)
return BE_SQLITE_ERROR;
m_idSet.insert(id);
}
else if(val.isString())
{
uint64_t id;
BentleyStatus status = BeStringUtilities::ParseUInt64(id, val.ToUtf8CP());
if(status != BentleyStatus::SUCCESS)
return BE_SQLITE_ERROR;
if(id == 0)
return BE_SQLITE_ERROR;
m_idSet.insert(id);
}
else
return BE_SQLITE_ERROR;
return BE_SQLITE_OK;
}

//---------------------------------------------------------------------------------------
ColinKerr marked this conversation as resolved.
Show resolved Hide resolved
// @bsimethod
//---------------------------------------------------------------------------------------
DbResult IdSetModule::IdSetTable::IdSetCursor::Filter(int idxNum, const char *idxStr, int argc, DbValue* argv) {
int recompute = false;
if( idxNum & 1 ){
if(argv[0].GetValueType() == DbValueType::TextVal) {
Utf8String valueGiven = argv[0].GetValueText();
if(!valueGiven.EqualsIAscii(m_text)) {
m_text = valueGiven;
recompute = true;
}
} else {
Reset();
}
} else {
Reset();
}
if(recompute)
{
m_idSet.clear();
BeJsDocument doc;
doc.Parse(m_text.c_str());

if(FilterJSONStringIntoArray(doc) != BE_SQLITE_OK)
{
Reset();
m_index = m_idSet.begin();
return BE_SQLITE_ERROR;
}
}
m_index = m_idSet.begin();
return BE_SQLITE_OK;
}

//---------------------------------------------------------------------------------------
// @bsimethod
//---------------------------------------------------------------------------------------
void IdSetModule::IdSetTable::IdSetCursor::Reset() {
m_text = "[]";
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; i<nConstraint; i++){
auto pConstraint = indexInfo.GetConstraint(i);
int iCol; /* 0 for start, 1 for stop, 2 for step */
int iMask; /* bitmask for those column */
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 ){
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 & ~idxNum)!=0 ){
return BE_SQLITE_CONSTRAINT;
}

indexInfo.SetEstimatedCost(0);
indexInfo.SetEstimatedRows(100);
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();
DbResult rcIdSet = (new IdSetModule(ecdb))->Register();
if(rc != BE_SQLITE_OK || rcIdSet != BE_SQLITE_OK)
return rc;
return BE_SQLITE_OK;
}
END_BENTLEY_SQLITE_EC_NAMESPACE
61 changes: 61 additions & 0 deletions iModelCore/ECDb/ECDb/BuiltInVTabs.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,67 @@ struct ClassPropsModule : BeSQLite::DbModule {
DbResult Connect(DbVirtualTable*& out, Config& conf, int argc, const char* const* argv) final;
};

struct IdSetModule : ECDbModule {
constexpr static auto NAME = "IdSet";
struct IdSetTable : ECDbVirtualTable {
struct IdSetCursor : ECDbCursor {

enum class Columns{
Id = 0,
Json_array_ids = 1,
};

private:
Utf8String m_text;
bset<uint64_t> m_idSet;
bset<uint64_t>::const_iterator m_index;

public:
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;
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:
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,
NAME,
"CREATE TABLE x(id, json_array_ids hidden)",
R"xml(<?xml version="1.0" encoding="utf-8" ?>
<ECSchema
schemaName="ECVLib"
alias="ECVLib"
version="1.0.0"
xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
<ECSchemaReference name="ECDbVirtual" version="01.00.00" alias="ecdbvir" />
<ECCustomAttributes>
<VirtualSchema xmlns="ECDbVirtual.01.00.00"/>
</ECCustomAttributes>
<ECEntityClass typeName="IdSet" modifier="Abstract">
<ECCustomAttributes>
<VirtualType xmlns="ECDbVirtual.01.00.00"/>
</ECCustomAttributes>
<ECProperty propertyName="id" typeName="long" extendedTypeName="Id"/>
</ECEntityClass>
</ECSchema>)xml"
) {}
DbResult Connect(DbVirtualTable*& out, Config& conf, int argc, const char* const* argv) final;
};


DbResult RegisterBuildInVTabs(ECDbR);

END_BENTLEY_SQLITE_EC_NAMESPACE
35 changes: 33 additions & 2 deletions iModelCore/ECDb/ECDb/ConcurrentQueryManagerImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1905,8 +1905,39 @@ 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<BeInt64Id>> idSet = std::make_shared<IdSet<BeInt64Id>>(param.GetValueIdSet());
st = stmt.BindVirtualSet(index, idSet);
BinderInfo const& binderInfo = stmt.GetBinderInfo(index);
if(binderInfo.GetType() == BinderInfo::BinderType::VirtualSet)
{
std::shared_ptr<IdSet<BeInt64Id>> idSet = std::make_shared<IdSet<BeInt64Id>>(param.GetValueIdSet());
st = stmt.BindVirtualSet(index, idSet);
}
else if(binderInfo.GetType() == BinderInfo::BinderType::Array && binderInfo.IsForIdSet())
{
bool allElementsAdded = true;
IECSqlBinder& binder = stmt.GetBinder(index);
IdSet<BeInt64Id> set(param.GetValueIdSet());
for(auto& ids: set)
{
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
st = ECSqlStatus::Success;
else
st = ECSqlStatus::Error;
}
else
st = ECSqlStatus::Error;
rschili marked this conversation as resolved.
Show resolved Hide resolved

break;
}
case ECSqlParam::Type::Integer:
Expand Down
Loading