From 7f93a16b5ecb229a3b9021166b7a97a4faad9045 Mon Sep 17 00:00:00 2001 From: "Sarunas.Griskus" Date: Mon, 25 Mar 2024 10:20:20 +0200 Subject: [PATCH 01/15] Branch off struct handling changes --- .../ECSql/ECInstanceECSqlSelectAdapter.cpp | 13 +++---- .../ECDb/PublicAPI/ECDb/ECInstanceAdapter.h | 2 +- .../Tests/NonPublished/JsonUpdaterTests.cpp | 39 +++++++++++++++++++ .../iModelPlatform/DgnCore/DgnElement.cpp | 33 +++++++--------- 4 files changed, 58 insertions(+), 29 deletions(-) diff --git a/iModelCore/ECDb/ECDb/ECSql/ECInstanceECSqlSelectAdapter.cpp b/iModelCore/ECDb/ECDb/ECSql/ECInstanceECSqlSelectAdapter.cpp index aa9df8814fd..9a0e36c781f 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECInstanceECSqlSelectAdapter.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ECInstanceECSqlSelectAdapter.cpp @@ -180,13 +180,10 @@ BentleyStatus ECInstanceECSqlSelectAdapter::SetPropertyData(IECInstanceR instanc if (prop->GetIsStruct()) { - for (IECSqlValue const& memberVal : value.GetStructIterable()) + if (SUCCESS != SetStructElement(ecVal, prop->GetAsStructProperty()->GetType(), value)) { - if (SUCCESS != SetPropertyData(instance, accessString.c_str(), memberVal)) - { - LOG.debugv("ECInstanceECSqlSelectAdapter::SetPropertyData - failed to set struct property %s", accessString.c_str()); - return ERROR; - } + LOG.debugv("ECInstanceECSqlSelectAdapter::SetPropertyData - failed to set struct property %s", accessString.c_str()); + return ERROR; } return SUCCESS; @@ -204,7 +201,7 @@ BentleyStatus ECInstanceECSqlSelectAdapter::SetPropertyData(IECInstanceR instanc { if (prop->GetIsStructArray()) { - if (SUCCESS != SetStructArrayElement(ecVal, prop->GetAsStructArrayProperty()->GetStructElementType(), arrayElementValue)) + if (SUCCESS != SetStructElement(ecVal, prop->GetAsStructArrayProperty()->GetStructElementType(), arrayElementValue)) { LOG.debugv("ECInstanceECSqlSelectAdapter::SetPropertyData - failed to set struct array element (%d) on property %s", arrayIndex, accessString.c_str()); return ERROR; @@ -320,7 +317,7 @@ BentleyStatus ECInstanceECSqlSelectAdapter::SetPrimitiveValue(ECValueR val, ECN: /*---------------------------------------------------------------------------------**//** * @bsimethod +---------------+---------------+---------------+---------------+---------------+------*/ -BentleyStatus ECInstanceECSqlSelectAdapter::SetStructArrayElement(ECValueR val, ECClassCR structType, IECSqlValue const& value) const +BentleyStatus ECInstanceECSqlSelectAdapter::SetStructElement(ECValueR val, ECClassCR structType, IECSqlValue const& value) const { val.Clear(); if (value.IsNull()) diff --git a/iModelCore/ECDb/PublicAPI/ECDb/ECInstanceAdapter.h b/iModelCore/ECDb/PublicAPI/ECDb/ECInstanceAdapter.h index a95b211d2f3..91e52df712a 100644 --- a/iModelCore/ECDb/PublicAPI/ECDb/ECInstanceAdapter.h +++ b/iModelCore/ECDb/PublicAPI/ECDb/ECInstanceAdapter.h @@ -43,7 +43,7 @@ struct ECInstanceECSqlSelectAdapter final BentleyStatus SetRelationshipSource(ECN::IECInstanceR instance, IECSqlValue const& value) const; BentleyStatus SetRelationshipTarget(ECN::IECInstanceR instance, IECSqlValue const& value) const; - BentleyStatus SetStructArrayElement(ECN::ECValueR val, ECN::ECClassCR structType, IECSqlValue const& value) const; + BentleyStatus SetStructElement(ECN::ECValueR val, ECN::ECClassCR structType, IECSqlValue const& value) const; BentleyStatus SetPrimitiveValue(ECN::ECValueR val, ECN::PrimitiveType primitiveType, IECSqlValue const& value) const; BentleyStatus SetNavigationValue(ECN::IECInstanceR instance, IECSqlValue const& value) const; ECN::IECInstancePtr FindRelationshipEndpoint(ECInstanceId endpointInstanceId, ECN::ECClassId endpointClassId, ECN::StandaloneECRelationshipInstance*, bool isSource) const; diff --git a/iModelCore/ECDb/Tests/NonPublished/JsonUpdaterTests.cpp b/iModelCore/ECDb/Tests/NonPublished/JsonUpdaterTests.cpp index 9b79b2f108d..b7125e32aac 100644 --- a/iModelCore/ECDb/Tests/NonPublished/JsonUpdaterTests.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/JsonUpdaterTests.cpp @@ -1021,4 +1021,43 @@ TEST_F(JsonUpdaterTests, UpdateTimeOfDayValues) EXPECT_EQ(JsonValue("[{\"StartTime\": \"00:00:00.000\", \"EndTime\":\"23:59:59.999\"}]"), GetHelper().ExecuteSelectECSql(Utf8PrintfString("SELECT StartTime,EndTime FROM ts.CalendarEntry WHERE ECInstanceId=%s", key2.GetInstanceId().ToString().c_str()).c_str())); } +//--------------------------------------------------------------------------------------- +// @bsimethod +//+---------------+---------------+---------------+---------------+---------------+------ +TEST_F(JsonUpdaterTests, UpdateStructPropertyToNull) + { + ASSERT_EQ(BentleyStatus::SUCCESS, SetupECDb("updateStructPropertyToNull.ecdb", SchemaItem(R"xml( + + + + + + + + + )xml"))); + + ECInstanceKey key; + { + // Insert test instance + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "INSERT INTO ts.TestClass(IntProp,ClassProp) VALUES(?,?)")); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindInt(1, 15)); + ASSERT_EQ(ECSqlStatus::Success, stmt.GetBinder(2)["MyStructNumber"].BindInt(17)); + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)); + } + + ECClassCP testClass = m_ecdb.Schemas().GetClass("TestSchema", "TestClass"); + ASSERT_TRUE(testClass != nullptr); + EXPECT_EQ(JsonValue("[{\"IntProp\":15,\"ClassProp\":{\"MyStructNumber\":17}}]"), GetHelper().ExecuteSelectECSql(Utf8PrintfString("SELECT IntProp,ClassProp FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()).c_str())); + + // Update test instance + JsonUpdater updater(m_ecdb, *testClass, nullptr); + ASSERT_TRUE(updater.IsValid()); + ASSERT_EQ(BE_SQLITE_OK, updater.Update(key.GetInstanceId(), JsonValue("{\"IntProp\": 6, \"ClassProp\": null}").m_value)); + + // Check for ClassProp to not exist (be null) + EXPECT_EQ(JsonValue("[{\"IntProp\":6}]"), GetHelper().ExecuteSelectECSql(Utf8PrintfString("SELECT IntProp,ClassProp FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()).c_str())); + } + END_ECDBUNITTESTS_NAMESPACE diff --git a/iModelCore/iModelPlatform/DgnCore/DgnElement.cpp b/iModelCore/iModelPlatform/DgnCore/DgnElement.cpp index 245748d0eef..b614b510810 100644 --- a/iModelCore/iModelPlatform/DgnCore/DgnElement.cpp +++ b/iModelCore/iModelPlatform/DgnCore/DgnElement.cpp @@ -1296,28 +1296,21 @@ void DgnElement::ToBaseJson(BeJsValue val) const { * @bsimethod +---------------+---------------+---------------+---------------+---------------+------*/ void DgnElement::_ToJson(BeJsValue val, BeJsConst opts) const { + CachedECSqlStatementPtr stmt = this->GetDgnDb().GetPreparedECSqlStatement("SELECT $ FROM Bis.Element WHERE ECInstanceId=?"); + if (!stmt.IsValid()) + { + BeAssert(false); + return; + } + stmt->BindId(1, m_elementId); - // all the base properties - ToBaseJson(val); - - // if auto-handled properties are already in memory - if (nullptr != m_ecPropertyData) { - ElementAutoHandledPropertiesECInstanceAdapterPtr autoHandledAdapter = ElementAutoHandledPropertiesECInstanceAdapter::Create(*this, true); - if (autoHandledAdapter.IsNull()) - return; - - std::function shouldWriteProperty = [this](Utf8CP propName) { - if (ECJsonSystemNames::IsTopLevelSystemMember(propName)) - return false; + if (BE_SQLITE_ROW != stmt->Step()) + { + BeAssert(false); + return; + } - ElementECPropertyAccessor const accessor(*this, propName); - return accessor.IsValid() && accessor.IsAutoHandled(); - }; - JsonEcInstanceWriter::WritePartialInstanceToJson(val, *autoHandledAdapter, JsonEcInstanceWriter::MemberNameCasing::LowerFirstChar, shouldWriteProperty, &GetDgnDb().GetClassLocater()); - } else if (GetElementId().IsValid()) { - // element must be persistent to use ECSql to load properties - autoHandlePropertiesToJsonFromECSql(val, *this); // auto-handled properties were not loaded, query for them - } + val = stmt->GetValueText(0); } /*---------------------------------------------------------------------------------**//** From bc3c3411be3d7da48bd66e2c810d70b284a9a6d8 Mon Sep 17 00:00:00 2001 From: "Sarunas.Griskus" Date: Mon, 25 Mar 2024 11:11:12 +0200 Subject: [PATCH 02/15] revert json changes --- .../iModelPlatform/DgnCore/DgnElement.cpp | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/iModelCore/iModelPlatform/DgnCore/DgnElement.cpp b/iModelCore/iModelPlatform/DgnCore/DgnElement.cpp index b614b510810..245748d0eef 100644 --- a/iModelCore/iModelPlatform/DgnCore/DgnElement.cpp +++ b/iModelCore/iModelPlatform/DgnCore/DgnElement.cpp @@ -1296,21 +1296,28 @@ void DgnElement::ToBaseJson(BeJsValue val) const { * @bsimethod +---------------+---------------+---------------+---------------+---------------+------*/ void DgnElement::_ToJson(BeJsValue val, BeJsConst opts) const { - CachedECSqlStatementPtr stmt = this->GetDgnDb().GetPreparedECSqlStatement("SELECT $ FROM Bis.Element WHERE ECInstanceId=?"); - if (!stmt.IsValid()) - { - BeAssert(false); - return; - } - stmt->BindId(1, m_elementId); - if (BE_SQLITE_ROW != stmt->Step()) - { - BeAssert(false); - return; - } + // all the base properties + ToBaseJson(val); - val = stmt->GetValueText(0); + // if auto-handled properties are already in memory + if (nullptr != m_ecPropertyData) { + ElementAutoHandledPropertiesECInstanceAdapterPtr autoHandledAdapter = ElementAutoHandledPropertiesECInstanceAdapter::Create(*this, true); + if (autoHandledAdapter.IsNull()) + return; + + std::function shouldWriteProperty = [this](Utf8CP propName) { + if (ECJsonSystemNames::IsTopLevelSystemMember(propName)) + return false; + + ElementECPropertyAccessor const accessor(*this, propName); + return accessor.IsValid() && accessor.IsAutoHandled(); + }; + JsonEcInstanceWriter::WritePartialInstanceToJson(val, *autoHandledAdapter, JsonEcInstanceWriter::MemberNameCasing::LowerFirstChar, shouldWriteProperty, &GetDgnDb().GetClassLocater()); + } else if (GetElementId().IsValid()) { + // element must be persistent to use ECSql to load properties + autoHandlePropertiesToJsonFromECSql(val, *this); // auto-handled properties were not loaded, query for them + } } /*---------------------------------------------------------------------------------**//** From 9f8cdaa5d7ef4c8ab43bfc1e03cc68dba62977eb Mon Sep 17 00:00:00 2001 From: "Sarunas.Griskus" Date: Mon, 25 Mar 2024 16:25:26 +0200 Subject: [PATCH 03/15] Revert the structArrayElement changes --- .../ECSql/ECInstanceECSqlSelectAdapter.cpp | 18 +++++++++++++----- .../ECDb/PublicAPI/ECDb/ECInstanceAdapter.h | 2 +- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/iModelCore/ECDb/ECDb/ECSql/ECInstanceECSqlSelectAdapter.cpp b/iModelCore/ECDb/ECDb/ECSql/ECInstanceECSqlSelectAdapter.cpp index 9a0e36c781f..0c546f6c0d4 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECInstanceECSqlSelectAdapter.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ECInstanceECSqlSelectAdapter.cpp @@ -180,10 +180,18 @@ BentleyStatus ECInstanceECSqlSelectAdapter::SetPropertyData(IECInstanceR instanc if (prop->GetIsStruct()) { - if (SUCCESS != SetStructElement(ecVal, prop->GetAsStructProperty()->GetType(), value)) + if (value.IsNull()) { - LOG.debugv("ECInstanceECSqlSelectAdapter::SetPropertyData - failed to set struct property %s", accessString.c_str()); - return ERROR; + return SUCCESS; + } + + for (IECSqlValue const& memberVal : value.GetStructIterable()) + { + if (SUCCESS != SetPropertyData(instance, accessString.c_str(), memberVal)) + { + LOG.debugv("ECInstanceECSqlSelectAdapter::SetPropertyData - failed to set struct property %s", accessString.c_str()); + return ERROR; + } } return SUCCESS; @@ -201,7 +209,7 @@ BentleyStatus ECInstanceECSqlSelectAdapter::SetPropertyData(IECInstanceR instanc { if (prop->GetIsStructArray()) { - if (SUCCESS != SetStructElement(ecVal, prop->GetAsStructArrayProperty()->GetStructElementType(), arrayElementValue)) + if (SUCCESS != SetStructArrayElement(ecVal, prop->GetAsStructArrayProperty()->GetStructElementType(), arrayElementValue)) { LOG.debugv("ECInstanceECSqlSelectAdapter::SetPropertyData - failed to set struct array element (%d) on property %s", arrayIndex, accessString.c_str()); return ERROR; @@ -317,7 +325,7 @@ BentleyStatus ECInstanceECSqlSelectAdapter::SetPrimitiveValue(ECValueR val, ECN: /*---------------------------------------------------------------------------------**//** * @bsimethod +---------------+---------------+---------------+---------------+---------------+------*/ -BentleyStatus ECInstanceECSqlSelectAdapter::SetStructElement(ECValueR val, ECClassCR structType, IECSqlValue const& value) const +BentleyStatus ECInstanceECSqlSelectAdapter::SetStructArrayElement(ECValueR val, ECClassCR structType, IECSqlValue const& value) const { val.Clear(); if (value.IsNull()) diff --git a/iModelCore/ECDb/PublicAPI/ECDb/ECInstanceAdapter.h b/iModelCore/ECDb/PublicAPI/ECDb/ECInstanceAdapter.h index 91e52df712a..a95b211d2f3 100644 --- a/iModelCore/ECDb/PublicAPI/ECDb/ECInstanceAdapter.h +++ b/iModelCore/ECDb/PublicAPI/ECDb/ECInstanceAdapter.h @@ -43,7 +43,7 @@ struct ECInstanceECSqlSelectAdapter final BentleyStatus SetRelationshipSource(ECN::IECInstanceR instance, IECSqlValue const& value) const; BentleyStatus SetRelationshipTarget(ECN::IECInstanceR instance, IECSqlValue const& value) const; - BentleyStatus SetStructElement(ECN::ECValueR val, ECN::ECClassCR structType, IECSqlValue const& value) const; + BentleyStatus SetStructArrayElement(ECN::ECValueR val, ECN::ECClassCR structType, IECSqlValue const& value) const; BentleyStatus SetPrimitiveValue(ECN::ECValueR val, ECN::PrimitiveType primitiveType, IECSqlValue const& value) const; BentleyStatus SetNavigationValue(ECN::IECInstanceR instance, IECSqlValue const& value) const; ECN::IECInstancePtr FindRelationshipEndpoint(ECInstanceId endpointInstanceId, ECN::ECClassId endpointClassId, ECN::StandaloneECRelationshipInstance*, bool isSource) const; From 0ef5deb7a8d746033444d3a9499a6f2cc20d56f1 Mon Sep 17 00:00:00 2001 From: "Sarunas.Griskus" Date: Thu, 28 Mar 2024 10:42:20 +0200 Subject: [PATCH 04/15] remove value.IsNull changes --- iModelCore/ECDb/ECDb/ECSql/ECInstanceECSqlSelectAdapter.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/iModelCore/ECDb/ECDb/ECSql/ECInstanceECSqlSelectAdapter.cpp b/iModelCore/ECDb/ECDb/ECSql/ECInstanceECSqlSelectAdapter.cpp index 0c546f6c0d4..aa9df8814fd 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECInstanceECSqlSelectAdapter.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ECInstanceECSqlSelectAdapter.cpp @@ -180,11 +180,6 @@ BentleyStatus ECInstanceECSqlSelectAdapter::SetPropertyData(IECInstanceR instanc if (prop->GetIsStruct()) { - if (value.IsNull()) - { - return SUCCESS; - } - for (IECSqlValue const& memberVal : value.GetStructIterable()) { if (SUCCESS != SetPropertyData(instance, accessString.c_str(), memberVal)) From 9d1a8fa8c96103d33d674fa91242b2953ca68449 Mon Sep 17 00:00:00 2001 From: "Sarunas.Griskus" Date: Thu, 28 Mar 2024 14:45:19 +0200 Subject: [PATCH 05/15] idea at null setting for structs --- .../ECSql/ECInstanceECSqlSelectAdapter.cpp | 23 ++++++++++++++----- .../ECDb/PublicAPI/ECDb/ECInstanceAdapter.h | 7 +++--- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/iModelCore/ECDb/ECDb/ECSql/ECInstanceECSqlSelectAdapter.cpp b/iModelCore/ECDb/ECDb/ECSql/ECInstanceECSqlSelectAdapter.cpp index aa9df8814fd..eeba7953497 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECInstanceECSqlSelectAdapter.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ECInstanceECSqlSelectAdapter.cpp @@ -144,7 +144,7 @@ BentleyStatus ECInstanceECSqlSelectAdapter::SetInstanceData(IECInstanceR instanc //-------------------------------------------------------------------------------------- // @bsimethod //+---------------+---------------+---------------+---------------+---------------+------ -BentleyStatus ECInstanceECSqlSelectAdapter::SetPropertyData(IECInstanceR instance, Utf8CP parentPropertyAccessString, IECSqlValue const& value) const +BentleyStatus ECInstanceECSqlSelectAdapter::SetPropertyData(IECInstanceR instance, Utf8CP parentPropertyAccessString, IECSqlValue const& value, bool isDeepNull) const { ECSqlColumnInfo const& columnInfo = value.GetColumnInfo(); ECPropertyCP prop = columnInfo.GetProperty(); @@ -166,7 +166,7 @@ BentleyStatus ECInstanceECSqlSelectAdapter::SetPropertyData(IECInstanceR instanc if (prop->GetIsPrimitive()) { auto primitiveProp = prop->GetAsPrimitiveProperty(); - SetPrimitiveValue(ecVal, primitiveProp->GetType(), value); + SetPrimitiveValue(ecVal, primitiveProp->GetType(), value, isDeepNull); ECObjectsStatus ecStatus = instance.SetInternalValue(accessString.c_str(), ecVal); if (ecStatus != ECObjectsStatus::Success && ecStatus != ECObjectsStatus::PropertyValueMatchesNoChange) { @@ -180,9 +180,10 @@ BentleyStatus ECInstanceECSqlSelectAdapter::SetPropertyData(IECInstanceR instanc if (prop->GetIsStruct()) { + auto structIsNull = value.IsNull() || isDeepNull; for (IECSqlValue const& memberVal : value.GetStructIterable()) { - if (SUCCESS != SetPropertyData(instance, accessString.c_str(), memberVal)) + if (SUCCESS != SetPropertyData(instance, accessString.c_str(), memberVal, structIsNull)) { LOG.debugv("ECInstanceECSqlSelectAdapter::SetPropertyData - failed to set struct property %s", accessString.c_str()); return ERROR; @@ -195,8 +196,18 @@ BentleyStatus ECInstanceECSqlSelectAdapter::SetPropertyData(IECInstanceR instanc if (prop->GetIsArray()) { const int arrayLength = value.GetArrayLength(); - if (arrayLength <= 0) + if (arrayLength <= 0 || isDeepNull) + { + ECObjectsStatus ecStatus = instance.ClearArray(accessString.c_str()); + if (ecStatus != ECObjectsStatus::Success && ecStatus != ECObjectsStatus::PropertyValueMatchesNoChange) + { + BeAssert(false); + LOG.debugv("ECInstanceECSqlSelectAdapter::SetPropertyData - failed to clear array for property %s. Error (%d)", accessString.c_str(), ecStatus); + return ERROR; + } + return SUCCESS; + } instance.AddArrayElements(accessString.c_str(), arrayLength); int arrayIndex = 0; @@ -239,9 +250,9 @@ BentleyStatus ECInstanceECSqlSelectAdapter::SetPropertyData(IECInstanceR instanc /*---------------------------------------------------------------------------------**//** * @bsimethod +---------------+---------------+---------------+---------------+---------------+------*/ -BentleyStatus ECInstanceECSqlSelectAdapter::SetPrimitiveValue(ECValueR val, ECN::PrimitiveType primitiveType, IECSqlValue const& value) const +BentleyStatus ECInstanceECSqlSelectAdapter::SetPrimitiveValue(ECValueR val, ECN::PrimitiveType primitiveType, IECSqlValue const& value, bool isDeepNull) const { - if (value.IsNull()) + if (value.IsNull() || isDeepNull) { val.SetToNull(); return SUCCESS; diff --git a/iModelCore/ECDb/PublicAPI/ECDb/ECInstanceAdapter.h b/iModelCore/ECDb/PublicAPI/ECDb/ECInstanceAdapter.h index a95b211d2f3..08afa2cbac3 100644 --- a/iModelCore/ECDb/PublicAPI/ECDb/ECInstanceAdapter.h +++ b/iModelCore/ECDb/PublicAPI/ECDb/ECInstanceAdapter.h @@ -38,13 +38,14 @@ struct ECInstanceECSqlSelectAdapter final BentleyStatus SetInstanceId(ECN::IECInstanceR instance, IECSqlValue const& value) const; BentleyStatus SetInstanceData(ECN::IECInstanceR instance, bool usesClassIdFilter) const; - BentleyStatus SetPropertyData(ECN::IECInstanceR instance, Utf8CP parentPropertyAccessString, IECSqlValue const& value) const; - BentleyStatus SetPropertyData(ECN::IECInstanceR instance, IECSqlValue const& value) const { return SetPropertyData(instance, nullptr, value); } + BentleyStatus SetPropertyData(ECN::IECInstanceR instance, Utf8CP parentPropertyAccessString, IECSqlValue const& value, bool isDeepNull = false) const; + BentleyStatus SetPropertyData(ECN::IECInstanceR instance, IECSqlValue const& value, bool isDeepNull) const { return SetPropertyData(instance, nullptr, value, isDeepNull); } + BentleyStatus SetPropertyData(ECN::IECInstanceR instance, IECSqlValue const& value) const { return SetPropertyData(instance, nullptr, value, false); } BentleyStatus SetRelationshipSource(ECN::IECInstanceR instance, IECSqlValue const& value) const; BentleyStatus SetRelationshipTarget(ECN::IECInstanceR instance, IECSqlValue const& value) const; BentleyStatus SetStructArrayElement(ECN::ECValueR val, ECN::ECClassCR structType, IECSqlValue const& value) const; - BentleyStatus SetPrimitiveValue(ECN::ECValueR val, ECN::PrimitiveType primitiveType, IECSqlValue const& value) const; + BentleyStatus SetPrimitiveValue(ECN::ECValueR val, ECN::PrimitiveType primitiveType, IECSqlValue const& value, bool isDeepNull = false) const; BentleyStatus SetNavigationValue(ECN::IECInstanceR instance, IECSqlValue const& value) const; ECN::IECInstancePtr FindRelationshipEndpoint(ECInstanceId endpointInstanceId, ECN::ECClassId endpointClassId, ECN::StandaloneECRelationshipInstance*, bool isSource) const; BentleyStatus CreateColumnHandlers(); From 15ce48127d6f72082b1bb7aa97f09d7fbe285e0a Mon Sep 17 00:00:00 2001 From: "Sarunas.Griskus" Date: Wed, 19 Jun 2024 12:13:11 +0300 Subject: [PATCH 06/15] Append ECSqlStatementTests for updating struct to null --- .../NonPublished/ECSqlStatementTests.cpp | 456 ++++++++++++++++++ 1 file changed, 456 insertions(+) diff --git a/iModelCore/ECDb/Tests/NonPublished/ECSqlStatementTests.cpp b/iModelCore/ECDb/Tests/NonPublished/ECSqlStatementTests.cpp index 8401cca7b9c..b42d67246f6 100644 --- a/iModelCore/ECDb/Tests/NonPublished/ECSqlStatementTests.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/ECSqlStatementTests.cpp @@ -11800,4 +11800,460 @@ TEST_F(ECSqlStatementTestFixture, SelectAnySomeAll) ASSERT_EQ(expected, GetHelper().ExecuteSelectECSql(ecsql)); } } + +//--------------------------------------------------------------------------------------- +// @bsimethod +//+---------------+---------------+---------------+---------------+---------------+------ +TEST_F(ECSqlStatementTestFixture, UpdateToNullBinding) + { + ASSERT_EQ(BentleyStatus::SUCCESS, SetupECDb("ec_sql_update_to_null.ecdb", SchemaItem(R"xml( + + + + + + + + + + + + + + + + + )xml"))); + + ///*** Insertable data + auto i = 123; + auto p2d = DPoint2d::From(23.22, 31.11); + + auto st_p2d = DPoint2d::From(53.22, 31.11); + + auto st_st_b = true; + auto st_st_i = 45; + + //*** Update all values to null + { + Utf8String ecsql; + ecsql.Sprintf("INSERT INTO ts.TestClass (p2d, st, array_i) VALUES (?,?,?)"); + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + stmt.BindPoint2d(1, p2d); + auto& st = stmt.GetBinder(2); + + st["p2d"].BindPoint2d(st_p2d); + st["st"]["b"].BindBoolean(st_st_b); + st["st"]["i"].BindBoolean(st_st_i); + + stmt.GetBinder(3).AddArrayElement().BindInt(i); + + ECInstanceKey key; + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); + stmt.Finalize(); + + ecsql.Sprintf("UPDATE ONLY ts.TestClass SET p2d=?, st=?, array_i=? WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + for (int i = 1; i <= 3; ++i) + { + ASSERT_EQ(ECSqlStatus::Success, stmt.GetBinder(i).BindNull()); + } + + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); + stmt.Finalize(); + + ecsql.Sprintf("SELECT p2d, p2d.X, p2d.Y, st, array_i FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); + for (int i = 0; i < stmt.GetColumnCount(); ++i) + { + ASSERT_TRUE(stmt.IsValueNull(i)) << "no values bound to " << stmt.GetECSql(); + } + + const int expectedMembersCount = (int) m_ecdb.Schemas().GetClass("TestSchema", "ComplexStruct")->GetPropertyCount(true); + IECSqlValue const& structVal = stmt.GetValue(3); + int actualMembersCount = 0; + for (IECSqlValue const& memberVal : structVal.GetStructIterable()) + { + actualMembersCount++; + ASSERT_TRUE(memberVal.IsNull()); + } + ASSERT_EQ(expectedMembersCount, actualMembersCount); + + IECSqlValue const& structArrayVal = stmt.GetValue(4); + ASSERT_EQ(0, structArrayVal.GetArrayLength()); + } + + //*** Update array to contain two null elements + { + Utf8String ecsql; + ecsql.Sprintf("INSERT INTO ts.TestClass (array_i) VALUES (?)"); + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + stmt.GetBinder(1).AddArrayElement().BindInt(i); + + ECInstanceKey key; + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); + stmt.Finalize(); + + ecsql.Sprintf("UPDATE ONLY ts.TestClass SET array_i=? WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindNull()); + ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindNull()); + + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); + stmt.Finalize(); + + ecsql.Sprintf("SELECT array_i FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); + + IECSqlValue const& val = stmt.GetValue(0); + ASSERT_FALSE(val.IsNull()) << stmt.GetECSql(); + ASSERT_EQ(2, val.GetArrayLength()); + for (IECSqlValue const& elementVal : val.GetArrayIterable()) + { + ASSERT_TRUE(elementVal.IsNull()) << stmt.GetECSql(); + + if (val.GetColumnInfo().GetDataType().IsStructArray()) + { + for (IECSqlValue const& memberVal : elementVal.GetStructIterable()) + { + ASSERT_TRUE(memberVal.IsNull()); + } + } + } + } + + // Update points to be partially unset + { + Utf8String ecsql; + ecsql.Sprintf("INSERT INTO ts.TestClass (p2d, st) VALUES (?,?)"); + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + stmt.BindPoint2d(1, p2d); + auto& st = stmt.GetBinder(2); + st["p2d"].BindPoint2d(st_p2d); + + ECInstanceKey key; + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); + stmt.Finalize(); + + ecsql.Sprintf("UPDATE ONLY ts.TestClass SET p2d.X=?, st.p2d.X=? WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + + ASSERT_EQ(ECSqlStatus::Success, stmt.BindNull(1)) << ecsql.c_str(); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindNull(2)) << ecsql.c_str(); + + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); + stmt.Finalize(); + + ecsql.Sprintf("SELECT p2d, p2d.X, p2d.Y, st.p2d, st.p2d.X, st.p2d.Y FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); + + std::set nullItems { "p2d", "p2d.X", "st.p2d", "st.p2d.X" }; + for (int i = 0; i < stmt.GetColumnCount(); i++) + { + IECSqlValue const& val = stmt.GetValue(i); + Utf8String propPath = val.GetColumnInfo().GetPropertyPath().ToString(); + const bool expectedToBeNull = nullItems.find(propPath) != nullItems.end(); + ASSERT_EQ(expectedToBeNull, val.IsNull()) << "Select clause item " << i << " in " << stmt.GetECSql(); + } + } + + //*** Update nested struct to be partially unset + { + Utf8String ecsql; + ecsql.Sprintf("INSERT INTO ts.TestClass (st) VALUES (?)"); + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + + auto& st = stmt.GetBinder(1); + st["st"]["b"].BindBoolean(st_st_b); + st["st"]["i"].BindBoolean(st_st_i); + + ECInstanceKey key; + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); + stmt.Finalize(); + + ecsql.Sprintf("UPDATE ONLY ts.TestClass SET st=? WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + + auto& elementBinder = stmt.GetBinder(1); + ASSERT_EQ(ECSqlStatus::Success, elementBinder["st"]["i"].BindNull()); // Set st.st.i = null + ASSERT_EQ(ECSqlStatus::Success, elementBinder["st"]["b"].BindBoolean(st_st_b)); // Set st.st.b = true, so that the whole structure doesn't become null + + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); + stmt.Finalize(); + + ecsql.Sprintf("SELECT st FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); + + ASSERT_FALSE(stmt.IsValueNull(0)); + IECSqlValue const& structVal = stmt.GetValue(0); + for (IECSqlValue const& memberVal : structVal.GetStructIterable()) + { + if (memberVal.GetColumnInfo().GetProperty()->GetName().Equals("st")) + { + int memberCount = 0; + for (IECSqlValue const& nestedMemberVal : memberVal.GetStructIterable()) + { + memberCount++; + if (nestedMemberVal.GetColumnInfo().GetProperty()->GetName().Equals("b")) + ASSERT_FALSE(nestedMemberVal.IsNull()); + else + ASSERT_TRUE(nestedMemberVal.IsNull()); + } + ASSERT_EQ((int) memberVal.GetColumnInfo().GetStructType()->GetPropertyCount(true), memberCount); + } + else + ASSERT_TRUE(memberVal.IsNull()); + } + } + + //*** Update nested struct to have all properties null + { + Utf8String ecsql; + ecsql.Sprintf("INSERT INTO ts.TestClass (st) VALUES (?)"); + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + + auto& st = stmt.GetBinder(1); + st["st"]["i"].BindInt(st_st_i); + st["st"]["b"].BindBoolean(st_st_b); + + ECInstanceKey key; + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); + stmt.Finalize(); + + ecsql.Sprintf("UPDATE ONLY ts.TestClass SET st.p2d=?, st.st.i=?, st.st.b=? " + "WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + + ASSERT_EQ(ECSqlStatus::Success, stmt.GetBinder(1).BindPoint2d(p2d)); // Make at least one arg in non-nested st not null. + ASSERT_EQ(ECSqlStatus::Success, stmt.GetBinder(2).BindNull()); + ASSERT_EQ(ECSqlStatus::Success, stmt.GetBinder(3).BindNull()); + + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); + stmt.Finalize(); + + ecsql.Sprintf("SELECT st FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); + + ASSERT_FALSE(stmt.IsValueNull(0)); + IECSqlValue const& structVal = stmt.GetValue(0); + for (IECSqlValue const& memberVal : structVal.GetStructIterable()) + { + if (memberVal.GetColumnInfo().GetProperty()->GetName().Equals("p2d")) + ASSERT_FALSE(memberVal.IsNull()); + else + ASSERT_TRUE(memberVal.IsNull()); // st will fall under this check + } + } + } + +//--------------------------------------------------------------------------------------- +// @bsimethod +//+---------------+---------------+---------------+---------------+---------------+------ +TEST_F(ECSqlStatementTestFixture, UpdateToNullInline) + { + ASSERT_EQ(BentleyStatus::SUCCESS, SetupECDb("ec_sql_update_to_null.ecdb", SchemaItem(R"xml( + + + + + + + + + + + + + + + + + )xml"))); + + ///*** Insertable data + auto i = 123; + auto p2d = DPoint2d::From(23.22, 31.11); + + auto st_p2d = DPoint2d::From(53.22, 31.11); + + auto st_st_b = true; + auto st_st_i = 45; + + //*** Update all values to null + { + Utf8String ecsql; + ecsql.Sprintf("INSERT INTO ts.TestClass (p2d, st, array_i) VALUES (?,?,?)"); + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + stmt.BindPoint2d(1, p2d); + auto& st = stmt.GetBinder(2); + + st["p2d"].BindPoint2d(st_p2d); + st["st"]["b"].BindBoolean(st_st_b); + st["st"]["i"].BindBoolean(st_st_i); + + stmt.GetBinder(3).AddArrayElement().BindInt(i); + + ECInstanceKey key; + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); + stmt.Finalize(); + + ecsql.Sprintf("UPDATE ONLY ts.TestClass SET p2d=NULL, st=NULL, array_i=NULL WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); + stmt.Finalize(); + + ecsql.Sprintf("SELECT p2d, p2d.X, p2d.Y, st, array_i FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); + for (int i = 0; i < stmt.GetColumnCount(); ++i) + { + ASSERT_TRUE(stmt.IsValueNull(i)) << "no values bound to " << stmt.GetECSql(); + } + + const int expectedMembersCount = (int) m_ecdb.Schemas().GetClass("TestSchema", "ComplexStruct")->GetPropertyCount(true); + IECSqlValue const& structVal = stmt.GetValue(3); + int actualMembersCount = 0; + for (IECSqlValue const& memberVal : structVal.GetStructIterable()) + { + actualMembersCount++; + ASSERT_TRUE(memberVal.IsNull()); + } + ASSERT_EQ(expectedMembersCount, actualMembersCount); + + IECSqlValue const& structArrayVal = stmt.GetValue(4); + ASSERT_EQ(0, structArrayVal.GetArrayLength()); + } + + // Update points to be partially unset + { + Utf8String ecsql; + ecsql.Sprintf("INSERT INTO ts.TestClass (p2d, st) VALUES (?,?)"); + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + stmt.BindPoint2d(1, p2d); + auto& st = stmt.GetBinder(2); + st["p2d"].BindPoint2d(st_p2d); + + ECInstanceKey key; + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); + stmt.Finalize(); + + ecsql.Sprintf("UPDATE ONLY ts.TestClass SET p2d.X=NULL, st.p2d.X=NULL WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); + stmt.Finalize(); + + ecsql.Sprintf("SELECT p2d, p2d.X, p2d.Y, st.p2d, st.p2d.X, st.p2d.Y FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); + + std::set nullItems { "p2d", "p2d.X", "st.p2d", "st.p2d.X" }; + for (int i = 0; i < stmt.GetColumnCount(); i++) + { + IECSqlValue const& val = stmt.GetValue(i); + Utf8String propPath = val.GetColumnInfo().GetPropertyPath().ToString(); + const bool expectedToBeNull = nullItems.find(propPath) != nullItems.end(); + ASSERT_EQ(expectedToBeNull, val.IsNull()) << "Select clause item " << i << " in " << stmt.GetECSql(); + } + } + + //*** Update nested struct to be partially unset + { + Utf8String ecsql; + ecsql.Sprintf("INSERT INTO ts.TestClass (st) VALUES (?)"); + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + + auto& st = stmt.GetBinder(1); + st["st"]["b"].BindBoolean(st_st_b); + st["st"]["i"].BindBoolean(st_st_i); + + ECInstanceKey key; + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); + stmt.Finalize(); + + ecsql.Sprintf("UPDATE ONLY ts.TestClass SET st.st.i=NULL, st.st.b=TRUE WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); + stmt.Finalize(); + + ecsql.Sprintf("SELECT st FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); + + ASSERT_FALSE(stmt.IsValueNull(0)); + IECSqlValue const& structVal = stmt.GetValue(0); + for (IECSqlValue const& memberVal : structVal.GetStructIterable()) + { + if (memberVal.GetColumnInfo().GetProperty()->GetName().Equals("st")) + { + int memberCount = 0; + for (IECSqlValue const& nestedMemberVal : memberVal.GetStructIterable()) + { + memberCount++; + if (nestedMemberVal.GetColumnInfo().GetProperty()->GetName().Equals("b")) + ASSERT_FALSE(nestedMemberVal.IsNull()); + else + ASSERT_TRUE(nestedMemberVal.IsNull()); + } + ASSERT_EQ((int) memberVal.GetColumnInfo().GetStructType()->GetPropertyCount(true), memberCount); + } + else + ASSERT_TRUE(memberVal.IsNull()); + } + } + + //*** Update nested struct to have all properties null + { + Utf8String ecsql; + ecsql.Sprintf("INSERT INTO ts.TestClass (st) VALUES (?)"); + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + + auto& st = stmt.GetBinder(1); + st["p2d"].BindPoint2d(p2d); + st["st"]["i"].BindInt(st_st_i); + st["st"]["b"].BindBoolean(st_st_b); + + ECInstanceKey key; + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); + stmt.Finalize(); + + ecsql.Sprintf("UPDATE ONLY ts.TestClass SET st.st.i=NULL, st.st.b=NULL " + "WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); + stmt.Finalize(); + + ecsql.Sprintf("SELECT st FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); + + ASSERT_FALSE(stmt.IsValueNull(0)); + IECSqlValue const& structVal = stmt.GetValue(0); + for (IECSqlValue const& memberVal : structVal.GetStructIterable()) + { + if (memberVal.GetColumnInfo().GetProperty()->GetName().Equals("p2d")) + ASSERT_FALSE(memberVal.IsNull()); + else + ASSERT_TRUE(memberVal.IsNull()); // st will fall under this check + } + } + } END_ECDBUNITTESTS_NAMESPACE From 2c725c207fc14265b048e70b65105c3869c7dc0e Mon Sep 17 00:00:00 2001 From: "Sarunas.Griskus" Date: Wed, 19 Jun 2024 17:09:47 +0300 Subject: [PATCH 07/15] Revert previous attempt --- .../ECSql/ECInstanceECSqlSelectAdapter.cpp | 23 +++++-------------- .../ECDb/PublicAPI/ECDb/ECInstanceAdapter.h | 7 +++--- 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/iModelCore/ECDb/ECDb/ECSql/ECInstanceECSqlSelectAdapter.cpp b/iModelCore/ECDb/ECDb/ECSql/ECInstanceECSqlSelectAdapter.cpp index eeba7953497..aa9df8814fd 100644 --- a/iModelCore/ECDb/ECDb/ECSql/ECInstanceECSqlSelectAdapter.cpp +++ b/iModelCore/ECDb/ECDb/ECSql/ECInstanceECSqlSelectAdapter.cpp @@ -144,7 +144,7 @@ BentleyStatus ECInstanceECSqlSelectAdapter::SetInstanceData(IECInstanceR instanc //-------------------------------------------------------------------------------------- // @bsimethod //+---------------+---------------+---------------+---------------+---------------+------ -BentleyStatus ECInstanceECSqlSelectAdapter::SetPropertyData(IECInstanceR instance, Utf8CP parentPropertyAccessString, IECSqlValue const& value, bool isDeepNull) const +BentleyStatus ECInstanceECSqlSelectAdapter::SetPropertyData(IECInstanceR instance, Utf8CP parentPropertyAccessString, IECSqlValue const& value) const { ECSqlColumnInfo const& columnInfo = value.GetColumnInfo(); ECPropertyCP prop = columnInfo.GetProperty(); @@ -166,7 +166,7 @@ BentleyStatus ECInstanceECSqlSelectAdapter::SetPropertyData(IECInstanceR instanc if (prop->GetIsPrimitive()) { auto primitiveProp = prop->GetAsPrimitiveProperty(); - SetPrimitiveValue(ecVal, primitiveProp->GetType(), value, isDeepNull); + SetPrimitiveValue(ecVal, primitiveProp->GetType(), value); ECObjectsStatus ecStatus = instance.SetInternalValue(accessString.c_str(), ecVal); if (ecStatus != ECObjectsStatus::Success && ecStatus != ECObjectsStatus::PropertyValueMatchesNoChange) { @@ -180,10 +180,9 @@ BentleyStatus ECInstanceECSqlSelectAdapter::SetPropertyData(IECInstanceR instanc if (prop->GetIsStruct()) { - auto structIsNull = value.IsNull() || isDeepNull; for (IECSqlValue const& memberVal : value.GetStructIterable()) { - if (SUCCESS != SetPropertyData(instance, accessString.c_str(), memberVal, structIsNull)) + if (SUCCESS != SetPropertyData(instance, accessString.c_str(), memberVal)) { LOG.debugv("ECInstanceECSqlSelectAdapter::SetPropertyData - failed to set struct property %s", accessString.c_str()); return ERROR; @@ -196,18 +195,8 @@ BentleyStatus ECInstanceECSqlSelectAdapter::SetPropertyData(IECInstanceR instanc if (prop->GetIsArray()) { const int arrayLength = value.GetArrayLength(); - if (arrayLength <= 0 || isDeepNull) - { - ECObjectsStatus ecStatus = instance.ClearArray(accessString.c_str()); - if (ecStatus != ECObjectsStatus::Success && ecStatus != ECObjectsStatus::PropertyValueMatchesNoChange) - { - BeAssert(false); - LOG.debugv("ECInstanceECSqlSelectAdapter::SetPropertyData - failed to clear array for property %s. Error (%d)", accessString.c_str(), ecStatus); - return ERROR; - } - + if (arrayLength <= 0) return SUCCESS; - } instance.AddArrayElements(accessString.c_str(), arrayLength); int arrayIndex = 0; @@ -250,9 +239,9 @@ BentleyStatus ECInstanceECSqlSelectAdapter::SetPropertyData(IECInstanceR instanc /*---------------------------------------------------------------------------------**//** * @bsimethod +---------------+---------------+---------------+---------------+---------------+------*/ -BentleyStatus ECInstanceECSqlSelectAdapter::SetPrimitiveValue(ECValueR val, ECN::PrimitiveType primitiveType, IECSqlValue const& value, bool isDeepNull) const +BentleyStatus ECInstanceECSqlSelectAdapter::SetPrimitiveValue(ECValueR val, ECN::PrimitiveType primitiveType, IECSqlValue const& value) const { - if (value.IsNull() || isDeepNull) + if (value.IsNull()) { val.SetToNull(); return SUCCESS; diff --git a/iModelCore/ECDb/PublicAPI/ECDb/ECInstanceAdapter.h b/iModelCore/ECDb/PublicAPI/ECDb/ECInstanceAdapter.h index 08afa2cbac3..a95b211d2f3 100644 --- a/iModelCore/ECDb/PublicAPI/ECDb/ECInstanceAdapter.h +++ b/iModelCore/ECDb/PublicAPI/ECDb/ECInstanceAdapter.h @@ -38,14 +38,13 @@ struct ECInstanceECSqlSelectAdapter final BentleyStatus SetInstanceId(ECN::IECInstanceR instance, IECSqlValue const& value) const; BentleyStatus SetInstanceData(ECN::IECInstanceR instance, bool usesClassIdFilter) const; - BentleyStatus SetPropertyData(ECN::IECInstanceR instance, Utf8CP parentPropertyAccessString, IECSqlValue const& value, bool isDeepNull = false) const; - BentleyStatus SetPropertyData(ECN::IECInstanceR instance, IECSqlValue const& value, bool isDeepNull) const { return SetPropertyData(instance, nullptr, value, isDeepNull); } - BentleyStatus SetPropertyData(ECN::IECInstanceR instance, IECSqlValue const& value) const { return SetPropertyData(instance, nullptr, value, false); } + BentleyStatus SetPropertyData(ECN::IECInstanceR instance, Utf8CP parentPropertyAccessString, IECSqlValue const& value) const; + BentleyStatus SetPropertyData(ECN::IECInstanceR instance, IECSqlValue const& value) const { return SetPropertyData(instance, nullptr, value); } BentleyStatus SetRelationshipSource(ECN::IECInstanceR instance, IECSqlValue const& value) const; BentleyStatus SetRelationshipTarget(ECN::IECInstanceR instance, IECSqlValue const& value) const; BentleyStatus SetStructArrayElement(ECN::ECValueR val, ECN::ECClassCR structType, IECSqlValue const& value) const; - BentleyStatus SetPrimitiveValue(ECN::ECValueR val, ECN::PrimitiveType primitiveType, IECSqlValue const& value, bool isDeepNull = false) const; + BentleyStatus SetPrimitiveValue(ECN::ECValueR val, ECN::PrimitiveType primitiveType, IECSqlValue const& value) const; BentleyStatus SetNavigationValue(ECN::IECInstanceR instance, IECSqlValue const& value) const; ECN::IECInstancePtr FindRelationshipEndpoint(ECInstanceId endpointInstanceId, ECN::ECClassId endpointClassId, ECN::StandaloneECRelationshipInstance*, bool isSource) const; BentleyStatus CreateColumnHandlers(); From c10966153aca033ca2c453d7a1b3c9ec62d71223 Mon Sep 17 00:00:00 2001 From: "Sarunas.Griskus" Date: Thu, 20 Jun 2024 18:17:51 +0300 Subject: [PATCH 08/15] Add struct = null handling --- .../PublicApi/ECObjects/ECJsonUtilities.h | 2 +- iModelCore/ecobjects/src/ECJsonUtilities.cpp | 62 +++++++++++++++++-- 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/iModelCore/ecobjects/PublicApi/ECObjects/ECJsonUtilities.h b/iModelCore/ecobjects/PublicApi/ECObjects/ECJsonUtilities.h index d62997f1802..0b3c5d34e32 100644 --- a/iModelCore/ecobjects/PublicApi/ECObjects/ECJsonUtilities.h +++ b/iModelCore/ecobjects/PublicApi/ECObjects/ECJsonUtilities.h @@ -474,7 +474,7 @@ struct JsonECInstanceConverter final ~JsonECInstanceConverter() = delete; //JsonCpp - static BentleyStatus JsonToECInstance(ECN::IECInstanceR, BeJsConst, ECN::ECClassCR currentClass, Utf8StringCR currentAccessString, IECClassLocaterR, bool ignoreUnknownProperties = false, IECSchemaRemapperCP remapper = nullptr, std::function shouldSerializeProperty = nullptr); + static BentleyStatus JsonToECInstance(ECN::IECInstanceR, BeJsConst, ECN::ECClassCR currentClass, Utf8StringCR currentAccessString, IECClassLocaterR, bool ignoreUnknownProperties = false, IECSchemaRemapperCP remapper = nullptr, std::function shouldSerializeProperty = nullptr, bool isDeepNull = false); static BentleyStatus JsonToPrimitiveECValue(ECN::ECValueR value, BeJsConst json, ECN::PrimitiveType type, Utf8CP extendedTypeName); static BentleyStatus JsonToArrayECValue(ECN::IECInstanceR, BeJsConst, ECN::ArrayECPropertyCR, Utf8StringCR currentAccessString, IECClassLocaterR); diff --git a/iModelCore/ecobjects/src/ECJsonUtilities.cpp b/iModelCore/ecobjects/src/ECJsonUtilities.cpp index ea7edf7f045..ee939d36cd7 100644 --- a/iModelCore/ecobjects/src/ECJsonUtilities.cpp +++ b/iModelCore/ecobjects/src/ECJsonUtilities.cpp @@ -706,13 +706,66 @@ BentleyStatus JsonECInstanceConverter::JsonToECInstance(ECN::IECInstanceR instan // @bsimethod //--------------------------------------------------------------------------------------- BentleyStatus JsonECInstanceConverter::JsonToECInstance(IECInstanceR instance, BeJsConst jsonValue, ECClassCR currentClass, Utf8StringCR currentAccessString, - IECClassLocaterR classLocater, bool ignoreUnknownProperties, IECSchemaRemapperCP remapper, std::function shouldSerializeProperty) + IECClassLocaterR classLocater, bool ignoreUnknownProperties, IECSchemaRemapperCP remapper, std::function shouldSerializeProperty, bool isDeepNull) { - if (!jsonValue.isObject()) + if (!jsonValue.isObject() && !isDeepNull) return ERROR; bool checkShouldSerializeProperty = shouldSerializeProperty != nullptr; BentleyStatus stat = SUCCESS; + + if (isDeepNull) + { + ClassLayoutR classLayout = currentClass.GetDefaultStandaloneEnabler()->GetClassLayout(); + uint32_t propIndex = 0; + if (ECObjectsStatus::Success != classLayout.GetPropertyIndex(propIndex, currentAccessString.c_str())) + return ERROR; + + uint32_t childIndex = classLayout.GetFirstChildPropertyIndex(propIndex); + while (0 != childIndex) + { + Utf8CP innerMemberName; + if (ECObjectsStatus::Success != classLayout.GetAccessStringByIndex(innerMemberName, childIndex)) + return ERROR; + + ECPropertyP ecProperty = currentClass.GetPropertyP(innerMemberName); + Utf8String childAccessString = currentAccessString.empty() ? Utf8String(innerMemberName) : currentAccessString + "." + Utf8String(innerMemberName); + if (ecProperty->GetIsPrimitive()) + { + ECValue ecValue; + ecValue.SetToNull(); + ECObjectsStatus ecStatus = instance.SetInternalValue(childAccessString.c_str(), ecValue); + if (ecStatus != ECObjectsStatus::Success && ecStatus != ECObjectsStatus::PropertyValueMatchesNoChange) + { + stat = ERROR; + BeAssert(ecStatus == ECObjectsStatus::Success || ecStatus == ECObjectsStatus::PropertyValueMatchesNoChange); + } + } + else if (ecProperty->GetIsStruct()) + { + BentleyStatus status = JsonToECInstance(instance, jsonValue, ecProperty->GetAsStructProperty()->GetType(), childAccessString, classLocater, ignoreUnknownProperties, remapper, nullptr, true); + if (status != SUCCESS) + { + stat = ERROR; + BeAssert(SUCCESS == status); + } + } + else if (ecProperty->GetIsArray()) + { + ECObjectsStatus ecStatus = instance.ClearArray(childAccessString.c_str()); + if (ECObjectsStatus::Success != ecStatus) + { + stat = ERROR; + BeAssert(ECObjectsStatus::Success == ecStatus); + } + } + + childIndex = classLayout.GetNextChildPropertyIndex(propIndex, childIndex); + } + + return stat; + } + jsonValue.ForEachProperty([&](Utf8CP memberName, BeJsConst childJsonValue) { auto convertOne = [&]() { @@ -747,10 +800,7 @@ BentleyStatus JsonECInstanceConverter::JsonToECInstance(IECInstanceR instance, B } else if (ecProperty->GetIsStruct()) { - if (childJsonValue.isNull()) - return false; - - if (SUCCESS != JsonToECInstance(instance, childJsonValue, ecProperty->GetAsStructProperty()->GetType(), accessString, classLocater, ignoreUnknownProperties, remapper)) + if (SUCCESS != JsonToECInstance(instance, childJsonValue, ecProperty->GetAsStructProperty()->GetType(), accessString, classLocater, ignoreUnknownProperties, remapper, nullptr, childJsonValue.isNull())) return true; return false; From 7d13e9bcbe887c4230083d7718df31b4772696f2 Mon Sep 17 00:00:00 2001 From: "Sarunas.Griskus" Date: Tue, 25 Jun 2024 20:05:17 +0300 Subject: [PATCH 09/15] address an issue where classLayout property is not found --- iModelCore/ecobjects/src/ECJsonUtilities.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iModelCore/ecobjects/src/ECJsonUtilities.cpp b/iModelCore/ecobjects/src/ECJsonUtilities.cpp index ee939d36cd7..bf4f1764e57 100644 --- a/iModelCore/ecobjects/src/ECJsonUtilities.cpp +++ b/iModelCore/ecobjects/src/ECJsonUtilities.cpp @@ -718,7 +718,7 @@ BentleyStatus JsonECInstanceConverter::JsonToECInstance(IECInstanceR instance, B { ClassLayoutR classLayout = currentClass.GetDefaultStandaloneEnabler()->GetClassLayout(); uint32_t propIndex = 0; - if (ECObjectsStatus::Success != classLayout.GetPropertyIndex(propIndex, currentAccessString.c_str())) + if (ECObjectsStatus::Success != classLayout.GetPropertyIndex(propIndex, "")) // we need to select the root of the currentClass return ERROR; uint32_t childIndex = classLayout.GetFirstChildPropertyIndex(propIndex); From eccdb160a38dd1a2bdb5928066f3d6790e0f86e3 Mon Sep 17 00:00:00 2001 From: "Sarunas.Griskus" Date: Wed, 26 Jun 2024 16:12:28 +0300 Subject: [PATCH 10/15] Test for the faulty behavior --- .../NonPublished/ECJsonUtilitiesTests.cpp | 417 ++++++++++++------ 1 file changed, 287 insertions(+), 130 deletions(-) diff --git a/iModelCore/ecobjects/test/NonPublished/ECJsonUtilitiesTests.cpp b/iModelCore/ecobjects/test/NonPublished/ECJsonUtilitiesTests.cpp index 3a49a7a261a..092b1fc07bc 100644 --- a/iModelCore/ecobjects/test/NonPublished/ECJsonUtilitiesTests.cpp +++ b/iModelCore/ecobjects/test/NonPublished/ECJsonUtilitiesTests.cpp @@ -15,7 +15,7 @@ struct ECJsonUtilitiesTestFixture : ECTestFixture { protected: - static BentleyStatus ParseJson(BeJsDocument& json, Utf8StringCR jsonStr) { json.Parse(jsonStr); return json.hasParseError() ? ERROR : SUCCESS; } + static BentleyStatus ParseJson(BeJsDocument& json, Utf8StringCR jsonStr) { json.Parse(jsonStr); return json.hasParseError() ? ERROR : SUCCESS; } static BentleyStatus ParseJson(rapidjson::Document& json, Utf8StringCR jsonStr) { return json.Parse<0>(jsonStr.c_str()).HasParseError() ? ERROR : SUCCESS; } }; @@ -29,71 +29,71 @@ void PrintTo(BeInt64Id id, std::ostream* os) { *os << id.GetValueUnchecked(); } //+---------------+---------------+---------------+---------------+---------------+------ TEST_F(ECJsonUtilitiesTestFixture, JsonToId) { - BeJsDocument jsonDoc; + BeJsDocument jsonDoc; rapidjson::Document rapidJson; //Ids formatted numerically in JSON Utf8CP jsonStr = "1234"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(BeInt64Id(UINT64_C(1234)), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(BeInt64Id(UINT64_C(1234)), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); EXPECT_EQ(BeInt64Id(UINT64_C(1234)), ECJsonUtilities::JsonToId(rapidJson)) << jsonStr; jsonStr = "-10"; // Not a valid value for an Id, but failing the method is not worth the overhead. So the test documents the behavior of the API - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(BeInt64Id(uint64_t(-10)), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(BeInt64Id(uint64_t(-10)), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); EXPECT_EQ(BeInt64Id(uint64_t(-10)), ECJsonUtilities::JsonToId(rapidJson)) << jsonStr; jsonStr = "3.14"; // Not a valid value for an Id, but failing the method is not worth the overhead. So the test documents the behavior of the API - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(BeInt64Id(3), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr << " floating numbers are rounded"; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(BeInt64Id(3), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr << " floating numbers are rounded"; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); EXPECT_EQ(BeInt64Id(3), ECJsonUtilities::JsonToId(rapidJson)) << jsonStr << " floating numbers are rounded"; //Ids formatted as decimal strings in JSON jsonStr = R"json("1234")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(BeInt64Id(UINT64_C(1234)), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(BeInt64Id(UINT64_C(1234)), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); EXPECT_EQ(BeInt64Id(UINT64_C(1234)), ECJsonUtilities::JsonToId(rapidJson)) << jsonStr; jsonStr = R"json("1099511627775")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(BeInt64Id(UINT64_C(1099511627775)), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(BeInt64Id(UINT64_C(1099511627775)), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); EXPECT_EQ(BeInt64Id(UINT64_C(1099511627775)), ECJsonUtilities::JsonToId(rapidJson)) << jsonStr; jsonStr = R"json("-10")json"; // Not a valid value for an Id, but failing the method is not worth the overhead. So the test documents the behavior of the API - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(BeInt64Id(uint64_t(-10)), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(BeInt64Id(uint64_t(-10)), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); EXPECT_EQ(BeInt64Id(uint64_t(-10)), ECJsonUtilities::JsonToId(rapidJson)) << jsonStr; jsonStr = R"json("3.14")json"; // Not a valid value for an Id, but failing the method is not worth the overhead. So the test documents the behavior of the API - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(BeInt64Id(3), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr << " floating numbers are rounded"; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(BeInt64Id(3), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr << " floating numbers are rounded"; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); EXPECT_EQ(BeInt64Id(3), ECJsonUtilities::JsonToId(rapidJson)) << jsonStr << " floating numbers are rounded"; //Ids formatted as hexadecimal strings in JSON jsonStr = R"json("0x123")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(BeInt64Id(UINT64_C(0x123)), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(BeInt64Id(UINT64_C(0x123)), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); EXPECT_EQ(BeInt64Id(UINT64_C(0x123)), ECJsonUtilities::JsonToId(rapidJson)) << jsonStr; jsonStr = R"json("0xFFFFFFFFFF")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(BeInt64Id(UINT64_C(0xFFFFFFFFFF)), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(BeInt64Id(UINT64_C(0xFFFFFFFFFF)), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); EXPECT_EQ(BeInt64Id(UINT64_C(0xFFFFFFFFFF)), ECJsonUtilities::JsonToId(rapidJson)) << jsonStr; @@ -104,14 +104,14 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToId) //+---------------+---------------+---------------+---------------+---------------+------ TEST_F(ECJsonUtilitiesTestFixture, IdToJson) { - BeJsDocument jsonDoc; + BeJsDocument jsonDoc; rapidjson::Document rapidJson; BeInt64Id id(UINT64_C(1234)); - ASSERT_EQ(SUCCESS, ECJsonUtilities::IdToJson(jsonDoc, id)) << id.ToString(); - ASSERT_TRUE(jsonDoc.isString()) << id.ToString(); - ASSERT_STRCASEEQ("0x4d2", jsonDoc.asCString()) << id.ToString(); + ASSERT_EQ(SUCCESS, ECJsonUtilities::IdToJson(jsonDoc, id)) << id.ToString(); + ASSERT_TRUE(jsonDoc.isString()) << id.ToString(); + ASSERT_STRCASEEQ("0x4d2", jsonDoc.asCString()) << id.ToString(); ASSERT_EQ(SUCCESS, ECJsonUtilities::IdToJson(rapidJson, id, rapidJson.GetAllocator())) << id.ToString(); ASSERT_TRUE(rapidJson.IsString()) << id.ToString(); @@ -119,9 +119,9 @@ TEST_F(ECJsonUtilitiesTestFixture, IdToJson) id = BeInt64Id(UINT64_C(1099511627775)); - ASSERT_EQ(SUCCESS, ECJsonUtilities::IdToJson(jsonDoc, id)) << id.ToString(); - ASSERT_TRUE(jsonDoc.isString()) << id.ToString(); - ASSERT_STRCASEEQ("0xffffffffff", jsonDoc.asCString()) << id.ToString(); + ASSERT_EQ(SUCCESS, ECJsonUtilities::IdToJson(jsonDoc, id)) << id.ToString(); + ASSERT_TRUE(jsonDoc.isString()) << id.ToString(); + ASSERT_STRCASEEQ("0xffffffffff", jsonDoc.asCString()) << id.ToString(); ASSERT_EQ(SUCCESS, ECJsonUtilities::IdToJson(rapidJson, id, rapidJson.GetAllocator())) << id.ToString(); ASSERT_TRUE(rapidJson.IsString()) << id.ToString(); @@ -133,7 +133,7 @@ TEST_F(ECJsonUtilitiesTestFixture, IdToJson) //+---------------+---------------+---------------+---------------+---------------+------ TEST_F(ECJsonUtilitiesTestFixture, JsonToInt64) { - BeJsDocument jsonDoc; + BeJsDocument jsonDoc; rapidjson::Document rapidJson; Utf8CP jsonStr = nullptr; @@ -141,8 +141,8 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToInt64) //formatted numerically in JSON jsonStr = "1234"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr; EXPECT_EQ(INT64_C(1234), val) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); @@ -150,8 +150,8 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToInt64) EXPECT_EQ(INT64_C(1234), val) << jsonStr; jsonStr = "-10"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr; EXPECT_EQ(INT64_C(-10), val) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); @@ -159,8 +159,8 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToInt64) EXPECT_EQ(INT64_C(-10), val) << jsonStr; jsonStr = "3.14"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr << " floating numbers are rounded"; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr << " floating numbers are rounded"; EXPECT_EQ(INT64_C(3), val) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); @@ -169,8 +169,8 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToInt64) //formatted as decimal strings in JSON jsonStr = R"json("1234")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr; EXPECT_EQ(INT64_C(1234), val) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); @@ -178,8 +178,8 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToInt64) EXPECT_EQ(INT64_C(1234), val) << jsonStr; jsonStr = R"json("1099511627775")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr; EXPECT_EQ(INT64_C(1099511627775), val) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); @@ -187,8 +187,8 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToInt64) EXPECT_EQ(INT64_C(1099511627775), val) << jsonStr; jsonStr = R"json("-10")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr; EXPECT_EQ(INT64_C(-10), val) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); @@ -196,8 +196,8 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToInt64) EXPECT_EQ(INT64_C(-10), val) << jsonStr; jsonStr = R"json("3.14")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr << " floating numbers are rounded"; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr << " floating numbers are rounded"; EXPECT_EQ(INT64_C(3), val) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); @@ -206,8 +206,8 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToInt64) //Ids formatted as hexadecimal strings in JSON jsonStr = R"json("0x123")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr; EXPECT_EQ(INT64_C(0x123), val) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); @@ -215,8 +215,8 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToInt64) EXPECT_EQ(INT64_C(0x123), val) << jsonStr; jsonStr = R"json("0xFFFFFFFFFF")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr; EXPECT_EQ(INT64_C(0xFFFFFFFFFF), val) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); @@ -229,32 +229,32 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToInt64) //+---------------+---------------+---------------+---------------+---------------+------ TEST_F(ECJsonUtilitiesTestFixture, Int64ToJson) { - BeJsDocument jsonDoc; + BeJsDocument jsonDoc; rapidjson::Document rapidJson; int64_t val = INT64_C(1234); - ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsNumber); - // TODO: add isIntegral() function - // ASSERT_TRUE(jsonDoc.isIntegral()) << val; - ASSERT_EQ(val, jsonDoc.asInt64()) << val; + ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsNumber); + // TODO: add isIntegral() function + // ASSERT_TRUE(jsonDoc.isIntegral()) << val; + ASSERT_EQ(val, jsonDoc.asInt64()) << val; ECJsonUtilities::Int64ToJson(rapidJson, val, rapidJson.GetAllocator(), ECJsonInt64Format::AsNumber); ASSERT_TRUE(rapidJson.IsInt64()) << val; ASSERT_EQ(val, rapidJson.GetInt64()) << val; - ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsDecimalString); - ASSERT_TRUE(jsonDoc.isString()) << val; - ASSERT_STRCASEEQ("1234", jsonDoc.asCString()) << val; + ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsDecimalString); + ASSERT_TRUE(jsonDoc.isString()) << val; + ASSERT_STRCASEEQ("1234", jsonDoc.asCString()) << val; ECJsonUtilities::Int64ToJson(rapidJson, val, rapidJson.GetAllocator(), ECJsonInt64Format::AsDecimalString); ASSERT_TRUE(rapidJson.IsString()) << val; ASSERT_STRCASEEQ("1234", rapidJson.GetString()) << val; - ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsHexadecimalString); - ASSERT_TRUE(jsonDoc.isString()) << val; - ASSERT_STRCASEEQ("0x4d2", jsonDoc.asCString()) << val; + ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsHexadecimalString); + ASSERT_TRUE(jsonDoc.isString()) << val; + ASSERT_STRCASEEQ("0x4d2", jsonDoc.asCString()) << val; ECJsonUtilities::Int64ToJson(rapidJson, val, rapidJson.GetAllocator(), ECJsonInt64Format::AsHexadecimalString); ASSERT_TRUE(rapidJson.IsString()) << val; @@ -262,27 +262,27 @@ TEST_F(ECJsonUtilitiesTestFixture, Int64ToJson) val = INT64_C(0xffffffffff); - ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsNumber); - // TODO: add isIntegral() function - // ASSERT_TRUE(jsonDoc.isIntegral()) << val; - ASSERT_EQ(val, jsonDoc.asInt64()) << val; + ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsNumber); + // TODO: add isIntegral() function + // ASSERT_TRUE(jsonDoc.isIntegral()) << val; + ASSERT_EQ(val, jsonDoc.asInt64()) << val; ECJsonUtilities::Int64ToJson(rapidJson, val, rapidJson.GetAllocator(), ECJsonInt64Format::AsNumber); ASSERT_TRUE(rapidJson.IsInt64()) << val; ASSERT_EQ(val, rapidJson.GetInt64()) << val; - ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsDecimalString); - ASSERT_TRUE(jsonDoc.isString()) << val; - ASSERT_STRCASEEQ("1099511627775", jsonDoc.asCString()) << val; + ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsDecimalString); + ASSERT_TRUE(jsonDoc.isString()) << val; + ASSERT_STRCASEEQ("1099511627775", jsonDoc.asCString()) << val; ECJsonUtilities::Int64ToJson(rapidJson, val, rapidJson.GetAllocator(), ECJsonInt64Format::AsDecimalString); ASSERT_TRUE(rapidJson.IsString()) << val; ASSERT_STRCASEEQ("1099511627775", rapidJson.GetString()) << val; - ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsHexadecimalString); - ASSERT_TRUE(jsonDoc.isString()) << val; - ASSERT_STRCASEEQ("0xffffffffff", jsonDoc.asCString()) << val; + ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsHexadecimalString); + ASSERT_TRUE(jsonDoc.isString()) << val; + ASSERT_STRCASEEQ("0xffffffffff", jsonDoc.asCString()) << val; ECJsonUtilities::Int64ToJson(rapidJson, val, rapidJson.GetAllocator(), ECJsonInt64Format::AsHexadecimalString); ASSERT_TRUE(rapidJson.IsString()) << val; @@ -291,18 +291,18 @@ TEST_F(ECJsonUtilitiesTestFixture, Int64ToJson) val = INT64_C(-10); - ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsNumber); - // TODO: add isIntegral() function - // ASSERT_TRUE(jsonDoc.isIntegral()) << val; - ASSERT_EQ(val, jsonDoc.asInt64()) << val; + ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsNumber); + // TODO: add isIntegral() function + // ASSERT_TRUE(jsonDoc.isIntegral()) << val; + ASSERT_EQ(val, jsonDoc.asInt64()) << val; ECJsonUtilities::Int64ToJson(rapidJson, val, rapidJson.GetAllocator(), ECJsonInt64Format::AsNumber); ASSERT_TRUE(rapidJson.IsInt64()) << val; ASSERT_EQ(val, rapidJson.GetInt64()) << val; - ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsDecimalString); - ASSERT_TRUE(jsonDoc.isString()) << val; - ASSERT_STRCASEEQ("-10", jsonDoc.asCString()) << val; + ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsDecimalString); + ASSERT_TRUE(jsonDoc.isString()) << val; + ASSERT_STRCASEEQ("-10", jsonDoc.asCString()) << val; ECJsonUtilities::Int64ToJson(rapidJson, val, rapidJson.GetAllocator(), ECJsonInt64Format::AsDecimalString); ASSERT_TRUE(rapidJson.IsString()) << val; @@ -314,15 +314,15 @@ TEST_F(ECJsonUtilitiesTestFixture, Int64ToJson) //+---------------+---------------+---------------+---------------+---------------+------ TEST_F(ECJsonUtilitiesTestFixture, JsonToDateTime) { - BeJsDocument jsonDoc; + BeJsDocument jsonDoc; rapidjson::Document rapidJson; Utf8CP jsonStr = nullptr; DateTime dt; jsonStr = R"json("2017-11-20T10:45:32.111Z")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToDateTime(dt, jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToDateTime(dt, jsonDoc)) << jsonStr; EXPECT_EQ(DateTime(DateTime::Kind::Utc, 2017, 11, 20, 10, 45, 32, 111), dt) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); @@ -330,8 +330,8 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToDateTime) EXPECT_EQ(DateTime(DateTime::Kind::Utc, 2017, 11, 20, 10, 45, 32, 111), dt) << jsonStr; jsonStr = R"json("2017-11-20 10:45:32.111Z")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToDateTime(dt, jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToDateTime(dt, jsonDoc)) << jsonStr; EXPECT_EQ(DateTime(DateTime::Kind::Utc, 2017, 11, 20, 10, 45, 32, 111), dt) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); @@ -339,8 +339,8 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToDateTime) EXPECT_EQ(DateTime(DateTime::Kind::Utc, 2017, 11, 20, 10, 45, 32, 111), dt) << jsonStr; jsonStr = R"json("2017-11-20T10:45:32.111")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToDateTime(dt, jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToDateTime(dt, jsonDoc)) << jsonStr; EXPECT_EQ(DateTime(DateTime::Kind::Unspecified, 2017, 11, 20, 10, 45, 32, 111), dt) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); @@ -348,8 +348,8 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToDateTime) EXPECT_EQ(DateTime(DateTime::Kind::Unspecified, 2017, 11, 20, 10, 45, 32, 111), dt) << jsonStr; jsonStr = R"json("2017-11-20 10:45:32.111")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToDateTime(dt, jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToDateTime(dt, jsonDoc)) << jsonStr; EXPECT_EQ(DateTime(DateTime::Kind::Unspecified, 2017, 11, 20, 10, 45, 32, 111), dt) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); @@ -357,8 +357,8 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToDateTime) EXPECT_EQ(DateTime(DateTime::Kind::Unspecified, 2017, 11, 20, 10, 45, 32, 111), dt) << jsonStr; jsonStr = R"json("2017-11-20")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToDateTime(dt, jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToDateTime(dt, jsonDoc)) << jsonStr; EXPECT_EQ(DateTime(2017, 11, 20), dt) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); @@ -371,14 +371,14 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToDateTime) //+---------------+---------------+---------------+---------------+---------------+------ TEST_F(ECJsonUtilitiesTestFixture, DateTimeToJson) { - BeJsDocument jsonDoc; + BeJsDocument jsonDoc; rapidjson::Document rapidJson; DateTime dt(DateTime::Kind::Utc, 2017, 11, 20, 10, 45, 32, 111); - ECJsonUtilities::DateTimeToJson(jsonDoc, dt); - ASSERT_TRUE(jsonDoc.isString()) << dt.ToString(); - ASSERT_STRCASEEQ("2017-11-20T10:45:32.111Z", jsonDoc.asCString()) << dt.ToString(); + ECJsonUtilities::DateTimeToJson(jsonDoc, dt); + ASSERT_TRUE(jsonDoc.isString()) << dt.ToString(); + ASSERT_STRCASEEQ("2017-11-20T10:45:32.111Z", jsonDoc.asCString()) << dt.ToString(); ECJsonUtilities::DateTimeToJson(rapidJson, dt, rapidJson.GetAllocator()); ASSERT_TRUE(rapidJson.IsString()) << dt.ToString(); @@ -386,9 +386,9 @@ TEST_F(ECJsonUtilitiesTestFixture, DateTimeToJson) dt = DateTime(DateTime::Kind::Unspecified, 2017, 11, 20, 10, 45, 32, 111); - ECJsonUtilities::DateTimeToJson(jsonDoc, dt); - ASSERT_TRUE(jsonDoc.isString()) << dt.ToString(); - ASSERT_STRCASEEQ("2017-11-20T10:45:32.111", jsonDoc.asCString()) << dt.ToString(); + ECJsonUtilities::DateTimeToJson(jsonDoc, dt); + ASSERT_TRUE(jsonDoc.isString()) << dt.ToString(); + ASSERT_STRCASEEQ("2017-11-20T10:45:32.111", jsonDoc.asCString()) << dt.ToString(); ECJsonUtilities::DateTimeToJson(rapidJson, dt, rapidJson.GetAllocator()); ASSERT_TRUE(rapidJson.IsString()) << dt.ToString(); @@ -396,9 +396,9 @@ TEST_F(ECJsonUtilitiesTestFixture, DateTimeToJson) dt = DateTime(2017, 11, 20); - ECJsonUtilities::DateTimeToJson(jsonDoc, dt); - ASSERT_TRUE(jsonDoc.isString()) << dt.ToString(); - ASSERT_STRCASEEQ("2017-11-20", jsonDoc.asCString()) << dt.ToString(); + ECJsonUtilities::DateTimeToJson(jsonDoc, dt); + ASSERT_TRUE(jsonDoc.isString()) << dt.ToString(); + ASSERT_STRCASEEQ("2017-11-20", jsonDoc.asCString()) << dt.ToString(); ECJsonUtilities::DateTimeToJson(rapidJson, dt, rapidJson.GetAllocator()); ASSERT_TRUE(rapidJson.IsString()) << dt.ToString(); @@ -410,8 +410,8 @@ TEST_F(ECJsonUtilitiesTestFixture, DateTimeToJson) //+---------------+---------------+---------------+---------------+---------------+------ TEST_F(ECJsonUtilitiesTestFixture, JsonToBinary) { - BeJsDocument jsonDocVal; - BeJsValue jsonDoc(jsonDocVal); + BeJsDocument jsonDocVal; + BeJsValue jsonDoc(jsonDocVal); rapidjson::Document rapidJsonVal; BeJsValue rapidJson(rapidJsonVal); @@ -422,11 +422,11 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToBinary) bvector actualByteVector; ByteStream actualByteStream; - jsonDoc.SetBinary(expectedByteVector); - EXPECT_EQ(SUCCESS, jsonDoc.GetBinary(actualByteVector)); + jsonDoc.SetBinary(expectedByteVector); + EXPECT_EQ(SUCCESS, jsonDoc.GetBinary(actualByteVector)); EXPECT_EQ(0, memcmp(expectedByteVector.data(), actualByteVector.data(), expectedByteVector.size())); - jsonDoc.SetBinary(expectedByteStream.data(), expectedByteStream.size()); - jsonDoc.GetBinary(actualByteStream); + jsonDoc.SetBinary(expectedByteStream.data(), expectedByteStream.size()); + jsonDoc.GetBinary(actualByteStream); EXPECT_EQ(0, memcmp(expectedByteStream.GetData(), actualByteStream.GetData(), expectedByteStream.GetSize())); actualByteVector.clear(); @@ -436,15 +436,15 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToBinary) EXPECT_EQ(0, memcmp(expectedByteVector.data(), actualByteVector.data(), expectedByteVector.size())); rapidJson.GetBinary(actualByteStream); EXPECT_EQ(0, memcmp(expectedByteStream.GetData(), actualByteStream.GetData(), expectedByteStream.GetSize())); - EXPECT_EQ(jsonDoc, rapidJson); + EXPECT_EQ(jsonDoc, rapidJson); - jsonDoc = "bad"; - jsonDoc.From(rapidJson); - EXPECT_EQ(jsonDoc, rapidJson); + jsonDoc = "bad"; + jsonDoc.From(rapidJson); + EXPECT_EQ(jsonDoc, rapidJson); rapidJson = "bad"; - rapidJson.From(jsonDoc); - EXPECT_EQ(jsonDoc, rapidJson); + rapidJson.From(jsonDoc); + EXPECT_EQ(jsonDoc, rapidJson); } //--------------------------------------------------------------------------------------- @@ -700,7 +700,7 @@ TEST_F(ECJsonUtilitiesTestFixture, IGeometryIModelJsonRoundTrip) struct JsonECInstanceConverterTestFixture : ECJsonUtilitiesTestFixture { protected: - BeJsDocument m_jsDoc; + BeJsDocument m_jsDoc; rapidjson::Document m_rapidJson; static IECInstancePtr CreateTestInstance(ECSchemaR schema, PrimitiveType propertyType) @@ -725,12 +725,12 @@ struct JsonECInstanceConverterTestFixture : ECJsonUtilitiesTestFixture { if (SUCCESS == expectedStatus) { - ASSERT_EQ(SUCCESS, ParseJson(m_jsDoc, jsonString)); + ASSERT_EQ(SUCCESS, ParseJson(m_jsDoc, jsonString)); ASSERT_EQ(SUCCESS, ParseJson(m_rapidJson, jsonString)); } else { - ASSERT_NE(SUCCESS, ParseJson(m_jsDoc, jsonString)); + ASSERT_NE(SUCCESS, ParseJson(m_jsDoc, jsonString)); ASSERT_NE(SUCCESS, ParseJson(m_rapidJson, jsonString)); } } @@ -785,43 +785,43 @@ TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_DoubleProperty) }; ParsePropertyJson("100"); - EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); EXPECT_EQ(100, GetPropertyValue(*instance)); EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_rapidJson, classLocater)); EXPECT_EQ(100, GetPropertyValue(*instance)); ParsePropertyJson("1.111111"); - EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); EXPECT_EQ(1.111111, GetPropertyValue(*instance)); EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_rapidJson, classLocater)); EXPECT_EQ(1.111111, GetPropertyValue(*instance)); ParsePropertyJson("1000000000000000"); - EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); EXPECT_EQ(1.0e15, GetPropertyValue(*instance)); EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_rapidJson, classLocater)); EXPECT_EQ(1.0e15, GetPropertyValue(*instance)); ParsePropertyJson("1000000000000"); - EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); EXPECT_EQ(1.0e12, GetPropertyValue(*instance)); EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_rapidJson, classLocater)); EXPECT_EQ(1.0e12, GetPropertyValue(*instance)); ParsePropertyJson("1000000000"); - EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); EXPECT_EQ(1.0e9, GetPropertyValue(*instance)); EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_rapidJson, classLocater)); EXPECT_EQ(1.0e9, GetPropertyValue(*instance)); ParsePropertyJson("1.0e18"); - EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); EXPECT_EQ(1.0e18, GetPropertyValue(*instance)); EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_rapidJson, classLocater)); EXPECT_EQ(1.0e18, GetPropertyValue(*instance)); ParsePropertyJson("1.0e-24"); - EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); EXPECT_EQ(1.0e-24, GetPropertyValue(*instance)); EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_rapidJson, classLocater)); EXPECT_EQ(1.0e-24, GetPropertyValue(*instance)); @@ -830,7 +830,7 @@ TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_DoubleProperty) ParsePropertyJson("\"ABCDE\""); { DISABLE_ASSERTS - EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_rapidJson, classLocater)); } #endif @@ -838,7 +838,7 @@ TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_DoubleProperty) ParsePropertyJson("\"1.111111\""); { DISABLE_ASSERTS - EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_rapidJson, classLocater)); } } @@ -863,7 +863,7 @@ TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_Struct) } IECInstancePtr structInstance = structClass->GetDefaultStandaloneEnabler()->CreateInstance(); EXPECT_TRUE(structInstance.IsValid()); - EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_jsDoc, classLocater)); // expect error when not initialized + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_jsDoc, classLocater)); // expect error when not initialized EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_rapidJson, classLocater)); // expect error when not initialized // construct entity instance with a struct property @@ -879,7 +879,7 @@ TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_Struct) } IECInstancePtr testInstance = testClass->GetDefaultStandaloneEnabler()->CreateInstance(); EXPECT_TRUE(testInstance.IsValid()); - EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); // expect error when not initialized + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); // expect error when not initialized EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); // expect error when not initialized // Test for expected JSON parse errors @@ -892,11 +892,11 @@ TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_Struct) //------------------------------------------------------------------------- ParseJsonString("null"); - EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_jsDoc, classLocater)); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_jsDoc, classLocater)); EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_rapidJson, classLocater)); ParseJsonString("{}"); - EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_jsDoc, classLocater)); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_jsDoc, classLocater)); AssertNullValue(*structInstance, "doubleProperty"); AssertNullValue(*structInstance, "stringProperty"); EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_rapidJson, classLocater)); @@ -904,7 +904,7 @@ TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_Struct) AssertNullValue(*structInstance, "stringProperty"); ParseJsonString(Utf8Chars(u8R"*({ "doubleProperty": 1.1, "stringProperty": "S1" })*")); - EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_jsDoc, classLocater)); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_jsDoc, classLocater)); AssertDoubleValue(*structInstance, "doubleProperty", 1.1); AssertStringValue(*structInstance, "stringProperty", "S1"); EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_rapidJson, classLocater)); @@ -916,11 +916,11 @@ TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_Struct) //------------------------------------------------------------------------- ParseJsonString("null"); - EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); ParseJsonString("{}"); - EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); AssertNullValue(*testInstance, "doubleProperty"); AssertNullValue(*testInstance, "stringProperty"); AssertNullValue(*testInstance, "testStruct"); @@ -934,7 +934,7 @@ TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_Struct) AssertNullValue(*testInstance, "testStruct.stringProperty"); ParseJsonString(Utf8Chars(u8R"*({ "doubleProperty": null, "stringProperty": null })*")); - EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); AssertNullValue(*testInstance, "doubleProperty"); AssertNullValue(*testInstance, "stringProperty"); AssertNullValue(*testInstance, "testStruct"); @@ -948,7 +948,7 @@ TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_Struct) AssertNullValue(*testInstance, "testStruct.stringProperty"); ParseJsonString(Utf8Chars(u8R"*({ "doubleProperty": 1.1, "stringProperty": "S1" })*")); - EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); AssertNullValue(*testInstance, "testStruct"); AssertNullValue(*testInstance, "testStruct.doubleProperty"); AssertNullValue(*testInstance, "testStruct.stringProperty"); @@ -962,7 +962,7 @@ TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_Struct) AssertStringValue(*testInstance, "stringProperty", "S1"); ParseJsonString(Utf8Chars(u8R"*({ "doubleProperty": 1.1, "stringProperty": "S1", "testStruct": null })*")); - EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); AssertNullValue(*testInstance, "testStruct"); AssertNullValue(*testInstance, "testStruct.doubleProperty"); AssertNullValue(*testInstance, "testStruct.stringProperty"); @@ -976,7 +976,7 @@ TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_Struct) AssertStringValue(*testInstance, "stringProperty", "S1"); ParseJsonString(Utf8Chars(u8R"*({ "doubleProperty": 1.1, "stringProperty": "S1", "testStruct": { "doubleProperty": 2.2, "stringProperty": "S2" } })*")); - EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); AssertDoubleValue(*testInstance, "doubleProperty", 1.1); AssertStringValue(*testInstance, "stringProperty", "S1"); AssertDoubleValue(*testInstance, "testStruct.doubleProperty", 2.2); @@ -988,4 +988,161 @@ TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_Struct) AssertStringValue(*testInstance, "testStruct.stringProperty", "S2"); } +//--------------------------------------------------------------------------------------- +// @bsimethod +//--------------------------------------------------------------------------------------- +TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_Struct_InitializedInstance) + { + ECSchemaPtr schema; + ECSchema::CreateSchema(schema, "Schema", "sch", 1, 0, 0); + InSchemaClassLocater classLocater(*schema); + + // construct struct instance + ECStructClassP structClass; + { + PrimitiveECPropertyP doubleProperty; + PrimitiveECPropertyP stringProperty; + schema->CreateStructClass(structClass, "TestStruct"); + structClass->CreatePrimitiveProperty(doubleProperty, "DoubleProperty", PRIMITIVETYPE_Double); + structClass->CreatePrimitiveProperty(stringProperty, "StringProperty", PRIMITIVETYPE_String); + } + IECInstancePtr structInstance = structClass->GetDefaultStandaloneEnabler()->CreateInstance(); + EXPECT_TRUE(structInstance.IsValid()); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_jsDoc, classLocater)); // expect error when not initialized + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_rapidJson, classLocater)); // expect error when not initialized + + // Initialize the struct instance with initial data + ECValue ecValue; + ecValue.SetDouble(22.2); + (*structInstance).SetInternalValue("DoubleProperty", ecValue); + ecValue.SetUtf8CP("TS.S1-Init"); + (*structInstance).SetInternalValue("StringProperty", ecValue); + + // construct entity instance with a struct property + ECEntityClassP testClass; + { + StructECPropertyP structProperty; + PrimitiveECPropertyP doubleProperty; + PrimitiveECPropertyP stringProperty; + schema->CreateEntityClass(testClass, "TestClass"); + testClass->CreateStructProperty(structProperty, "TestStruct", *structClass); + testClass->CreatePrimitiveProperty(doubleProperty, "DoubleProperty", PRIMITIVETYPE_Double); + testClass->CreatePrimitiveProperty(stringProperty, "StringProperty", PRIMITIVETYPE_String); + } + + IECInstancePtr testInstance = testClass->GetDefaultStandaloneEnabler()->CreateInstance(); + EXPECT_TRUE(testInstance.IsValid()); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); // expect error when not initialized + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); // expect error when not initialized + + // Initialize the test instance with initial data + ecValue.SetDouble(0.6); + (*testInstance).SetInternalValue("DoubleProperty", ecValue); + ecValue.SetUtf8CP("S1-Init"); + (*testInstance).SetInternalValue("StringProperty", ecValue); + ecValue.SetDouble(22.2); + (*testInstance).SetInternalValue("TestStruct.DoubleProperty", ecValue); + ecValue.SetUtf8CP("TS.S1-Init"); + (*testInstance).SetInternalValue("TestStruct.StringProperty", ecValue); + + // Test for expected JSON parse errors + ParseJsonString(nullptr, ERROR); + ParseJsonString("", ERROR); + ParseJsonString("undefined", ERROR); + + //------------------------------------------------------------------------- + // JSON --> struct instance tests + //------------------------------------------------------------------------- + + ParseJsonString("null"); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_jsDoc, classLocater)); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_rapidJson, classLocater)); + + ParseJsonString("{}"); // properties should remain unchanged + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_jsDoc, classLocater)); + AssertDoubleValue(*structInstance, "doubleProperty", 22.2); + AssertStringValue(*structInstance, "stringProperty", "TS.S1-Init"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_rapidJson, classLocater)); + AssertDoubleValue(*structInstance, "doubleProperty", 22.2); + AssertStringValue(*structInstance, "stringProperty", "TS.S1-Init"); + + ParseJsonString(Utf8Chars(u8R"*({ "doubleProperty": 22.3, "stringProperty": "TS.S1-New" })*")); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_jsDoc, classLocater)); + AssertDoubleValue(*structInstance, "doubleProperty", 22.3); + AssertStringValue(*structInstance, "stringProperty", "TS.S1-New"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_rapidJson, classLocater)); + AssertDoubleValue(*structInstance, "doubleProperty", 22.3); + AssertStringValue(*structInstance, "stringProperty", "TS.S1-New"); + + //------------------------------------------------------------------------- + // JSON --> entity instance with struct property tests + //------------------------------------------------------------------------- + + ParseJsonString("null"); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + + ParseJsonString("{}"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + AssertDoubleValue(*testInstance, "doubleProperty", 0.6); + AssertStringValue(*testInstance, "stringProperty", "S1-Init"); + AssertDoubleValue(*testInstance, "testStruct.doubleProperty", 22.2); + AssertStringValue(*testInstance, "testStruct.stringProperty", "TS.S1-Init"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + AssertDoubleValue(*testInstance, "doubleProperty", 0.6); + AssertStringValue(*testInstance, "stringProperty", "S1-Init"); + AssertDoubleValue(*testInstance, "testStruct.doubleProperty", 22.2); + AssertStringValue(*testInstance, "testStruct.stringProperty", "TS.S1-Init"); + + ParseJsonString(Utf8Chars(u8R"*({ "doubleProperty": null, "stringProperty": null })*")); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + AssertNullValue(*testInstance, "doubleProperty"); + AssertNullValue(*testInstance, "stringProperty"); + AssertDoubleValue(*testInstance, "testStruct.doubleProperty", 22.2); + AssertStringValue(*testInstance, "testStruct.stringProperty", "TS.S1-Init"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + AssertNullValue(*testInstance, "doubleProperty"); + AssertNullValue(*testInstance, "stringProperty"); + AssertDoubleValue(*testInstance, "testStruct.doubleProperty", 22.2); + AssertStringValue(*testInstance, "testStruct.stringProperty", "TS.S1-Init"); + + ParseJsonString(Utf8Chars(u8R"*({ "doubleProperty": 4.2, "stringProperty": "S1-new-new" })*")); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + AssertDoubleValue(*testInstance, "doubleProperty", 4.2); + AssertStringValue(*testInstance, "stringProperty", "S1-new-new"); + AssertDoubleValue(*testInstance, "testStruct.doubleProperty", 22.2); + AssertStringValue(*testInstance, "testStruct.stringProperty", "TS.S1-Init"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + AssertDoubleValue(*testInstance, "doubleProperty", 4.2); + AssertStringValue(*testInstance, "stringProperty", "S1-new-new"); + AssertDoubleValue(*testInstance, "testStruct.doubleProperty", 22.2); + AssertStringValue(*testInstance, "testStruct.stringProperty", "TS.S1-Init"); + + ParseJsonString(Utf8Chars(u8R"*({ "doubleProperty": 5.3, "stringProperty": "S1-new-new-new", "testStruct": null })*")); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + AssertDoubleValue(*testInstance, "doubleProperty", 5.3); + AssertStringValue(*testInstance, "stringProperty", "S1-new-new-new"); + AssertNullValue(*testInstance, "testStruct"); + AssertNullValue(*testInstance, "testStruct.doubleProperty"); + AssertNullValue(*testInstance, "testStruct.stringProperty"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + AssertDoubleValue(*testInstance, "doubleProperty", 5.3); + AssertStringValue(*testInstance, "stringProperty", "S1-new-new-new"); + AssertNullValue(*testInstance, "testStruct"); + AssertNullValue(*testInstance, "testStruct.doubleProperty"); + AssertNullValue(*testInstance, "testStruct.stringProperty"); + + ParseJsonString(Utf8Chars(u8R"*({ "doubleProperty": 111.1, "stringProperty": "S1", "testStruct": { "doubleProperty": 4.4, "stringProperty": "S2" } })*")); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + AssertDoubleValue(*testInstance, "doubleProperty", 111.1); + AssertStringValue(*testInstance, "stringProperty", "S1"); + AssertDoubleValue(*testInstance, "testStruct.doubleProperty", 4.4); + AssertStringValue(*testInstance, "testStruct.stringProperty", "S2"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + AssertDoubleValue(*testInstance, "doubleProperty", 111.1); + AssertStringValue(*testInstance, "stringProperty", "S1"); + AssertDoubleValue(*testInstance, "testStruct.doubleProperty", 4.4); + AssertStringValue(*testInstance, "testStruct.stringProperty", "S2"); + } + END_BENTLEY_ECN_TEST_NAMESPACE \ No newline at end of file From c7acc84c593434f5fdb3379752ebf000c60bce1c Mon Sep 17 00:00:00 2001 From: "Sarunas.Griskus" Date: Thu, 27 Jun 2024 09:43:01 +0300 Subject: [PATCH 11/15] Add JsonUpdater test for setting an array to null --- .../Tests/NonPublished/JsonUpdaterTests.cpp | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/iModelCore/ECDb/Tests/NonPublished/JsonUpdaterTests.cpp b/iModelCore/ECDb/Tests/NonPublished/JsonUpdaterTests.cpp index b7125e32aac..d3f4b85ee3b 100644 --- a/iModelCore/ECDb/Tests/NonPublished/JsonUpdaterTests.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/JsonUpdaterTests.cpp @@ -1060,4 +1060,48 @@ TEST_F(JsonUpdaterTests, UpdateStructPropertyToNull) EXPECT_EQ(JsonValue("[{\"IntProp\":6}]"), GetHelper().ExecuteSelectECSql(Utf8PrintfString("SELECT IntProp,ClassProp FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()).c_str())); } +//--------------------------------------------------------------------------------------- +// @bsimethod +//+---------------+---------------+---------------+---------------+---------------+------ +TEST_F(JsonUpdaterTests, UpdateArrayPropertyToNull) + { + ASSERT_EQ(BentleyStatus::SUCCESS, SetupECDb("updateArrayPropertyToNull.ecdb", SchemaItem(R"xml( + + + + + + + + + + )xml"))); + + ECInstanceKey key; + { + // Insert test instance + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "INSERT INTO ts.TestClass(IntProp,ArrBoolProp,ArrStructProp) VALUES(?,?,?)")); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindInt(1, 15)); + ASSERT_EQ(ECSqlStatus::Success, stmt.GetBinder(2).AddArrayElement().BindBoolean(true)); + ASSERT_EQ(ECSqlStatus::Success, stmt.GetBinder(2).AddArrayElement().BindBoolean(false)); + ASSERT_EQ(ECSqlStatus::Success, stmt.GetBinder(3).AddArrayElement()["DeepNumber"].BindInt(17)); + ASSERT_EQ(ECSqlStatus::Success, stmt.GetBinder(3).AddArrayElement()["DeepNumber"].BindInt(5)); + ASSERT_EQ(ECSqlStatus::Success, stmt.GetBinder(3).AddArrayElement()["DeepNumber"].BindInt(12)); + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)); + } + + ECClassCP testClass = m_ecdb.Schemas().GetClass("TestSchema", "TestClass"); + ASSERT_TRUE(testClass != nullptr); + EXPECT_EQ(JsonValue("[{\"IntProp\":15,\"ArrBoolProp\":[true, false],\"ArrStructProp\":[{\"DeepNumber\":17},{\"DeepNumber\":5},{\"DeepNumber\":12}]}]"), GetHelper().ExecuteSelectECSql(Utf8PrintfString("SELECT IntProp,ArrBoolProp,ArrStructProp FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()).c_str())); + + // Update test instance + JsonUpdater updater(m_ecdb, *testClass, nullptr); + ASSERT_TRUE(updater.IsValid()); + ASSERT_EQ(BE_SQLITE_OK, updater.Update(key.GetInstanceId(), JsonValue("{\"IntProp\":6,\"ArrBoolProp\": null, \"ArrStructProp\": null}").m_value)); + + // Check for ArrBoolProp and ArrStructProp to not exist (be null) + EXPECT_EQ(JsonValue("[{\"IntProp\":6}]"), GetHelper().ExecuteSelectECSql(Utf8PrintfString("SELECT IntProp,ArrBoolProp,ArrStructProp FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()).c_str())); + } + END_ECDBUNITTESTS_NAMESPACE From 694416705f873df54d6cfd5d25904b795215bd3f Mon Sep 17 00:00:00 2001 From: "Sarunas.Griskus" Date: Fri, 28 Jun 2024 16:46:34 +0300 Subject: [PATCH 12/15] ECJsonUtilities array handling + testing --- iModelCore/ecobjects/src/ECJsonUtilities.cpp | 31 +- .../NonPublished/ECJsonUtilitiesTests.cpp | 315 ++++++++++++++++++ 2 files changed, 324 insertions(+), 22 deletions(-) diff --git a/iModelCore/ecobjects/src/ECJsonUtilities.cpp b/iModelCore/ecobjects/src/ECJsonUtilities.cpp index bf4f1764e57..39c21985422 100644 --- a/iModelCore/ecobjects/src/ECJsonUtilities.cpp +++ b/iModelCore/ecobjects/src/ECJsonUtilities.cpp @@ -807,9 +807,6 @@ BentleyStatus JsonECInstanceConverter::JsonToECInstance(IECInstanceR instance, B } else if (ecProperty->GetIsArray()) { - if (childJsonValue.isNull()) - return false; - if (SUCCESS != JsonToArrayECValue(instance, childJsonValue, *ecProperty->GetAsArrayProperty(), accessString, classLocater)) return true; } @@ -1045,28 +1042,18 @@ BentleyStatus JsonECInstanceConverter::JsonToPrimitiveECValue(ECValueR ecValue, //--------------------------------------------------------------------------------------- BentleyStatus JsonECInstanceConverter::JsonToArrayECValue(IECInstanceR instance, BeJsConst jsonValue, ArrayECPropertyCR property, Utf8StringCR accessString, IECClassLocaterR classLocater) { - if (!jsonValue.isArray()) + if (!jsonValue.isArray() && !jsonValue.isNull()) return ERROR; - const uint32_t length = jsonValue.size(); + if (ECObjectsStatus::Success != instance.ClearArray(accessString.c_str())) + return ERROR; - ECValue arrayValue; - instance.GetValue(arrayValue, accessString.c_str()); - uint32_t currentLength = arrayValue.IsNull()? 0: arrayValue.GetArrayInfo().GetCount(); - if (length < currentLength) - { - // We need to shorten the array. Start by emptying it out. - if (ECObjectsStatus::Success != instance.ClearArray(accessString.c_str())) - return ERROR; - currentLength = 0; - // Now make the array the size we need - } - if (length > currentLength) - { - uint32_t xlength = length - currentLength; - if (ECObjectsStatus::Success != instance.AddArrayElements(accessString.c_str(), xlength)) - return ERROR; - } + if (jsonValue.isNull()) + return SUCCESS; + + const uint32_t length = jsonValue.size(); + if (length > 0 && ECObjectsStatus::Success != instance.AddArrayElements(accessString.c_str(), length)) + return ERROR; if (property.GetIsStructArray()) { diff --git a/iModelCore/ecobjects/test/NonPublished/ECJsonUtilitiesTests.cpp b/iModelCore/ecobjects/test/NonPublished/ECJsonUtilitiesTests.cpp index 092b1fc07bc..e085513a33a 100644 --- a/iModelCore/ecobjects/test/NonPublished/ECJsonUtilitiesTests.cpp +++ b/iModelCore/ecobjects/test/NonPublished/ECJsonUtilitiesTests.cpp @@ -755,6 +755,14 @@ struct JsonECInstanceConverterTestFixture : ECJsonUtilitiesTestFixture ASSERT_EQ(ECObjectsStatus::Success, instance.GetValue(ecValue, accessString)); ASSERT_TRUE(ecValue.IsNull()); } + + void AssertEmptyArray(IECInstanceCR instance, Utf8CP accessString) + { + ECValue ecValue; + ASSERT_EQ(ECObjectsStatus::Success, instance.GetValue(ecValue, accessString)); + ASSERT_TRUE(ecValue.IsArray()); + ASSERT_EQ(0, ecValue.GetArrayInfo().GetCount()); + } }; struct InSchemaClassLocater final : ECN::IECClassLocater @@ -1145,4 +1153,311 @@ TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_Struct_InitializedIn AssertStringValue(*testInstance, "testStruct.stringProperty", "S2"); } +//--------------------------------------------------------------------------------------- +// @bsimethod +//--------------------------------------------------------------------------------------- +TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_Array) + { + ECSchemaPtr schema; + ECSchema::CreateSchema(schema, "Schema", "sch", 1, 0, 0); + InSchemaClassLocater classLocater(*schema); + + // construct struct instance + ECStructClassP structClass; + { + PrimitiveECPropertyP doubleProperty; + PrimitiveECPropertyP stringProperty; + schema->CreateStructClass(structClass, "TestStruct"); + structClass->CreatePrimitiveProperty(doubleProperty, "DoubleProperty", PRIMITIVETYPE_Double); + structClass->CreatePrimitiveProperty(stringProperty, "StringProperty", PRIMITIVETYPE_String); + } + + ECEntityClassP testClass; + PrimitiveArrayECPropertyP testIntegerArrayProperty; + StructArrayECPropertyP testStructArrayProperty; + schema->CreateEntityClass(testClass, "TestClass"); + testClass->CreatePrimitiveArrayProperty(testIntegerArrayProperty, "IntArrayProp", PRIMITIVETYPE_Integer); + testClass->CreateStructArrayProperty(testStructArrayProperty, "StructArrayProp", *structClass); + + IECInstancePtr testInstance = testClass->GetDefaultStandaloneEnabler()->CreateInstance(); + EXPECT_TRUE(testInstance.IsValid()); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); // expect error when not initialized + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); // expect error when not initialized + + // Test for expected JSON parse errors + ParseJsonString(nullptr, ERROR); + ParseJsonString("", ERROR); + ParseJsonString("undefined", ERROR); + + //------------------------------------------------------------------------- + // JSON --> entity instance with array property tests + //------------------------------------------------------------------------- + + ParseJsonString("null"); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + + ParseJsonString("{}"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + AssertEmptyArray(*testInstance, "IntArrayProp"); + AssertEmptyArray(*testInstance, "StructArrayProp"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + AssertEmptyArray(*testInstance, "IntArrayProp"); + AssertEmptyArray(*testInstance, "StructArrayProp"); + + ParseJsonString(Utf8Chars(u8R"*({ "IntArrayProp": null, "StructArrayProp": null })*")); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + AssertEmptyArray(*testInstance, "IntArrayProp"); + AssertEmptyArray(*testInstance, "StructArrayProp"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + AssertEmptyArray(*testInstance, "IntArrayProp"); + AssertEmptyArray(*testInstance, "StructArrayProp"); + + ParseJsonString(Utf8Chars(u8R"*({ "IntArrayProp": [], "StructArrayProp": [] })*")); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + AssertEmptyArray(*testInstance, "IntArrayProp"); + AssertEmptyArray(*testInstance, "StructArrayProp"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + AssertEmptyArray(*testInstance, "IntArrayProp"); + AssertEmptyArray(*testInstance, "StructArrayProp"); + + ParseJsonString(Utf8Chars(u8R"*({ "IntArrayProp": [4, 5, 6, 9, 12], "StructArrayProp": [{ "DoubleProperty": 3.41, "StringProperty": "NewVal1" }, { "DoubleProperty": 12.92, "StringProperty": null }] })*")); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + { + ECValue intArrayProp; + ECValue structArrayProp; + IECInstancePtr structInst; + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp")); + ASSERT_EQ(intArrayProp.GetArrayInfo().GetCount(), 5); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 0)); + ASSERT_EQ(intArrayProp.GetInteger(), 4); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 1)); + ASSERT_EQ(intArrayProp.GetInteger(), 5); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 2)); + ASSERT_EQ(intArrayProp.GetInteger(), 6); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 3)); + ASSERT_EQ(intArrayProp.GetInteger(), 9); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 4)); + ASSERT_EQ(intArrayProp.GetInteger(), 12); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp")); + ASSERT_EQ(structArrayProp.GetArrayInfo().GetCount(), 2); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp", 0)); + structInst = structArrayProp.GetStruct(); + AssertDoubleValue(*structInst, "DoubleProperty", 3.41); + AssertStringValue(*structInst, "StringProperty", "NewVal1"); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp", 1)); + structInst = structArrayProp.GetStruct(); + AssertDoubleValue(*structInst, "DoubleProperty", 12.92); + AssertNullValue(*structInst, "StringProperty"); + } + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + { + ECValue intArrayProp; + ECValue structArrayProp; + IECInstancePtr structInst; + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp")); + ASSERT_EQ(intArrayProp.GetArrayInfo().GetCount(), 5); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 0)); + ASSERT_EQ(intArrayProp.GetInteger(), 4); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 1)); + ASSERT_EQ(intArrayProp.GetInteger(), 5); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 2)); + ASSERT_EQ(intArrayProp.GetInteger(), 6); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 3)); + ASSERT_EQ(intArrayProp.GetInteger(), 9); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 4)); + ASSERT_EQ(intArrayProp.GetInteger(), 12); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp")); + ASSERT_EQ(structArrayProp.GetArrayInfo().GetCount(), 2); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp", 0)); + structInst = structArrayProp.GetStruct(); + AssertDoubleValue(*structInst, "DoubleProperty", 3.41); + AssertStringValue(*structInst, "StringProperty", "NewVal1"); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp", 1)); + structInst = structArrayProp.GetStruct(); + AssertDoubleValue(*structInst, "DoubleProperty", 12.92); + AssertNullValue(*structInst, "StringProperty"); + } + } + +//--------------------------------------------------------------------------------------- +// @bsimethod +//--------------------------------------------------------------------------------------- +TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_Array_InitializedInstance) + { + ECSchemaPtr schema; + ECSchema::CreateSchema(schema, "Schema", "sch", 1, 0, 0); + InSchemaClassLocater classLocater(*schema); + + // construct struct instance + ECStructClassP structClass; + { + PrimitiveECPropertyP doubleProperty; + PrimitiveECPropertyP stringProperty; + schema->CreateStructClass(structClass, "TestStruct"); + structClass->CreatePrimitiveProperty(doubleProperty, "DoubleProperty", PRIMITIVETYPE_Double); + structClass->CreatePrimitiveProperty(stringProperty, "StringProperty", PRIMITIVETYPE_String); + } + + ECEntityClassP testClass; + PrimitiveArrayECPropertyP testIntegerArrayProperty; + StructArrayECPropertyP testStructArrayProperty; + schema->CreateEntityClass(testClass, "TestClass"); + testClass->CreatePrimitiveArrayProperty(testIntegerArrayProperty, "IntArrayProp", PRIMITIVETYPE_Integer); + testClass->CreateStructArrayProperty(testStructArrayProperty, "StructArrayProp", *structClass); + IECInstancePtr testInstance = testClass->GetDefaultStandaloneEnabler()->CreateInstance(); + EXPECT_TRUE(testInstance.IsValid()); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); // expect error when not initialized + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); // expect error when not initialized + + // Initialize the test instance with initial data + ECValue ecValue; + ecValue.SetInteger(9); + (*testInstance).AddArrayElements("IntArrayProp", 3); + (*testInstance).SetInternalValue("IntArrayProp", ecValue, 0); + ecValue.SetInteger(16); + (*testInstance).SetInternalValue("IntArrayProp", ecValue, 1); + ecValue.SetInteger(25); + (*testInstance).SetInternalValue("IntArrayProp", ecValue, 2); + + (*testInstance).AddArrayElements("StructArrayProp", 1); + IECInstancePtr structInstance = structClass->GetDefaultStandaloneEnabler()->CreateInstance(); + EXPECT_TRUE(structInstance.IsValid()); + ecValue.SetDouble(22.2); + (*structInstance).SetInternalValue("DoubleProperty", ecValue); + ecValue.SetUtf8CP("TS1-Init"); + (*structInstance).SetInternalValue("StringProperty", ecValue); + ecValue.SetStruct(structInstance.get()); + (*testInstance).SetInternalValue("StructArrayProp", ecValue, 0); + + // Test for expected JSON parse errors + ParseJsonString(nullptr, ERROR); + ParseJsonString("", ERROR); + ParseJsonString("undefined", ERROR); + + //------------------------------------------------------------------------- + // JSON --> entity instance with array property tests + //------------------------------------------------------------------------- + + ParseJsonString("null"); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + + ParseJsonString("{}"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + { + ECValue intArrayProp; + ECValue structArrayProp; + IECInstancePtr structInst; + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp")); + ASSERT_EQ(intArrayProp.GetArrayInfo().GetCount(), 3); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 0)); + ASSERT_EQ(intArrayProp.GetInteger(), 9); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 1)); + ASSERT_EQ(intArrayProp.GetInteger(), 16); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 2)); + ASSERT_EQ(intArrayProp.GetInteger(), 25); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp")); + ASSERT_EQ(structArrayProp.GetArrayInfo().GetCount(), 1); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp", 0)); + structInst = structArrayProp.GetStruct(); + AssertDoubleValue(*structInst, "DoubleProperty", 22.2); + AssertStringValue(*structInst, "StringProperty", "TS1-Init"); + } + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + { + ECValue intArrayProp; + ECValue structArrayProp; + IECInstancePtr structInst; + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp")); + ASSERT_EQ(intArrayProp.GetArrayInfo().GetCount(), 3); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 0)); + ASSERT_EQ(intArrayProp.GetInteger(), 9); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 1)); + ASSERT_EQ(intArrayProp.GetInteger(), 16); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 2)); + ASSERT_EQ(intArrayProp.GetInteger(), 25); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp")); + ASSERT_EQ(structArrayProp.GetArrayInfo().GetCount(), 1); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp", 0)); + structInst = structArrayProp.GetStruct(); + AssertDoubleValue(*structInst, "DoubleProperty", 22.2); + AssertStringValue(*structInst, "StringProperty", "TS1-Init"); + } + + ParseJsonString(Utf8Chars(u8R"*({ "IntArrayProp": null, "StructArrayProp": null })*")); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + AssertEmptyArray(*testInstance, "IntArrayProp"); + AssertEmptyArray(*testInstance, "StructArrayProp"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + AssertEmptyArray(*testInstance, "IntArrayProp"); + AssertEmptyArray(*testInstance, "StructArrayProp"); + + ParseJsonString(Utf8Chars(u8R"*({ "IntArrayProp": [4, 5, 6, 9, 12], "StructArrayProp": [{ "DoubleProperty": 3.41, "StringProperty": "NewVal1" }, { "DoubleProperty": 12.92, "StringProperty": null }] })*")); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + { + ECValue intArrayProp; + ECValue structArrayProp; + IECInstancePtr structInst; + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp")); + ASSERT_EQ(intArrayProp.GetArrayInfo().GetCount(), 5); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 0)); + ASSERT_EQ(intArrayProp.GetInteger(), 4); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 1)); + ASSERT_EQ(intArrayProp.GetInteger(), 5); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 2)); + ASSERT_EQ(intArrayProp.GetInteger(), 6); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 3)); + ASSERT_EQ(intArrayProp.GetInteger(), 9); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 4)); + ASSERT_EQ(intArrayProp.GetInteger(), 12); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp")); + ASSERT_EQ(structArrayProp.GetArrayInfo().GetCount(), 2); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp", 0)); + structInst = structArrayProp.GetStruct(); + AssertDoubleValue(*structInst, "DoubleProperty", 3.41); + AssertStringValue(*structInst, "StringProperty", "NewVal1"); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp", 1)); + structInst = structArrayProp.GetStruct(); + AssertDoubleValue(*structInst, "DoubleProperty", 12.92); + AssertNullValue(*structInst, "StringProperty"); + } + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + { + ECValue intArrayProp; + ECValue structArrayProp; + IECInstancePtr structInst; + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp")); + ASSERT_EQ(intArrayProp.GetArrayInfo().GetCount(), 5); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 0)); + ASSERT_EQ(intArrayProp.GetInteger(), 4); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 1)); + ASSERT_EQ(intArrayProp.GetInteger(), 5); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 2)); + ASSERT_EQ(intArrayProp.GetInteger(), 6); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 3)); + ASSERT_EQ(intArrayProp.GetInteger(), 9); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 4)); + ASSERT_EQ(intArrayProp.GetInteger(), 12); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp")); + ASSERT_EQ(structArrayProp.GetArrayInfo().GetCount(), 2); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp", 0)); + structInst = structArrayProp.GetStruct(); + AssertDoubleValue(*structInst, "DoubleProperty", 3.41); + AssertStringValue(*structInst, "StringProperty", "NewVal1"); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp", 1)); + structInst = structArrayProp.GetStruct(); + AssertDoubleValue(*structInst, "DoubleProperty", 12.92); + AssertNullValue(*structInst, "StringProperty"); + } + + ParseJsonString(Utf8Chars(u8R"*({ "IntArrayProp": [], "StructArrayProp": [] })*")); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + AssertEmptyArray(*testInstance, "IntArrayProp"); + AssertEmptyArray(*testInstance, "StructArrayProp"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + AssertEmptyArray(*testInstance, "IntArrayProp"); + AssertEmptyArray(*testInstance, "StructArrayProp"); + } + END_BENTLEY_ECN_TEST_NAMESPACE \ No newline at end of file From 9735bc1661dc344aad88e029e033866a6e5b777c Mon Sep 17 00:00:00 2001 From: "Sarunas.Griskus" Date: Mon, 1 Jul 2024 11:41:14 +0300 Subject: [PATCH 13/15] Do not clear instance array value if it's already empty --- iModelCore/ecobjects/src/ECJsonUtilities.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/iModelCore/ecobjects/src/ECJsonUtilities.cpp b/iModelCore/ecobjects/src/ECJsonUtilities.cpp index 39c21985422..3267e01de86 100644 --- a/iModelCore/ecobjects/src/ECJsonUtilities.cpp +++ b/iModelCore/ecobjects/src/ECJsonUtilities.cpp @@ -1045,7 +1045,10 @@ BentleyStatus JsonECInstanceConverter::JsonToArrayECValue(IECInstanceR instance, if (!jsonValue.isArray() && !jsonValue.isNull()) return ERROR; - if (ECObjectsStatus::Success != instance.ClearArray(accessString.c_str())) + ECValue arrayValue; + instance.GetValue(arrayValue, accessString.c_str()); + uint32_t currentLength = arrayValue.IsNull() ? 0 : arrayValue.GetArrayInfo().GetCount(); + if (currentLength != 0 && ECObjectsStatus::Success != instance.ClearArray(accessString.c_str())) return ERROR; if (jsonValue.isNull()) From 42dfb11d2e211c660ad2f348385adbe67364c53a Mon Sep 17 00:00:00 2001 From: "Sarunas.Griskus" Date: Tue, 2 Jul 2024 15:17:34 +0300 Subject: [PATCH 14/15] Use SqlPrintfString for SQL query strings --- .../NonPublished/ECSqlStatementTests.cpp | 152 ++++++++---------- .../Tests/NonPublished/JsonUpdaterTests.cpp | 26 ++- 2 files changed, 86 insertions(+), 92 deletions(-) diff --git a/iModelCore/ECDb/Tests/NonPublished/ECSqlStatementTests.cpp b/iModelCore/ECDb/Tests/NonPublished/ECSqlStatementTests.cpp index b42d67246f6..a5afbeb212b 100644 --- a/iModelCore/ECDb/Tests/NonPublished/ECSqlStatementTests.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/ECSqlStatementTests.cpp @@ -11828,43 +11828,40 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullBinding) ///*** Insertable data auto i = 123; auto p2d = DPoint2d::From(23.22, 31.11); - auto st_p2d = DPoint2d::From(53.22, 31.11); - auto st_st_b = true; auto st_st_i = 45; //*** Update all values to null { - Utf8String ecsql; - ecsql.Sprintf("INSERT INTO ts.TestClass (p2d, st, array_i) VALUES (?,?,?)"); + SqlPrintfString insertEcsql("INSERT INTO ts.TestClass (p2d, st, array_i) VALUES (?,?,?)"); ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.GetUtf8CP())) << insertEcsql.GetUtf8CP(); stmt.BindPoint2d(1, p2d); auto& st = stmt.GetBinder(2); - st["p2d"].BindPoint2d(st_p2d); st["st"]["b"].BindBoolean(st_st_b); st["st"]["i"].BindBoolean(st_st_i); - stmt.GetBinder(3).AddArrayElement().BindInt(i); ECInstanceKey key; ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); stmt.Finalize(); - ecsql.Sprintf("UPDATE ONLY ts.TestClass SET p2d=?, st=?, array_i=? WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + SqlPrintfString updateEcsql("UPDATE ONLY ts.TestClass SET p2d=?, st=?, array_i=? WHERE ECInstanceId=?", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, updateEcsql.GetUtf8CP())) << updateEcsql.GetUtf8CP(); for (int i = 1; i <= 3; ++i) { - ASSERT_EQ(ECSqlStatus::Success, stmt.GetBinder(i).BindNull()); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindNull(i)); } + ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(4, key.GetInstanceId())); ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); stmt.Finalize(); - ecsql.Sprintf("SELECT p2d, p2d.X, p2d.Y, st, array_i FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + SqlPrintfString selectEcsql("SELECT p2d, p2d.X, p2d.Y, st, array_i FROM ts.TestClass WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, selectEcsql.GetUtf8CP())) << selectEcsql.GetUtf8CP(); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(1, key.GetInstanceId())); ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); for (int i = 0; i < stmt.GetColumnCount(); ++i) { @@ -11887,27 +11884,28 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullBinding) //*** Update array to contain two null elements { - Utf8String ecsql; - ecsql.Sprintf("INSERT INTO ts.TestClass (array_i) VALUES (?)"); + SqlPrintfString insertEcsql("INSERT INTO ts.TestClass (array_i) VALUES (?)"); ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.GetUtf8CP())) << insertEcsql.GetUtf8CP(); stmt.GetBinder(1).AddArrayElement().BindInt(i); ECInstanceKey key; ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); stmt.Finalize(); - ecsql.Sprintf("UPDATE ONLY ts.TestClass SET array_i=? WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + SqlPrintfString updateEcsql("UPDATE ONLY ts.TestClass SET array_i=? WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, updateEcsql.GetUtf8CP())) << updateEcsql.GetUtf8CP(); IECSqlBinder& arrayBinder = stmt.GetBinder(1); ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindNull()); ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindNull()); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(2, key.GetInstanceId())); ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); stmt.Finalize(); - ecsql.Sprintf("SELECT array_i FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + SqlPrintfString selectEcsql("SELECT array_i FROM ts.TestClass WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, selectEcsql.GetUtf8CP())) << selectEcsql.GetUtf8CP(); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(1, key.GetInstanceId())); ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); IECSqlValue const& val = stmt.GetValue(0); @@ -11929,10 +11927,9 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullBinding) // Update points to be partially unset { - Utf8String ecsql; - ecsql.Sprintf("INSERT INTO ts.TestClass (p2d, st) VALUES (?,?)"); + SqlPrintfString insertEcsql("INSERT INTO ts.TestClass (p2d, st) VALUES (?,?)"); ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.GetUtf8CP())) << insertEcsql.GetUtf8CP(); stmt.BindPoint2d(1, p2d); auto& st = stmt.GetBinder(2); st["p2d"].BindPoint2d(st_p2d); @@ -11941,17 +11938,19 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullBinding) ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); stmt.Finalize(); - ecsql.Sprintf("UPDATE ONLY ts.TestClass SET p2d.X=?, st.p2d.X=? WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + SqlPrintfString updateEcsql("UPDATE ONLY ts.TestClass SET p2d.X=?, st.p2d.X=? WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, updateEcsql.GetUtf8CP())) << updateEcsql.GetUtf8CP(); - ASSERT_EQ(ECSqlStatus::Success, stmt.BindNull(1)) << ecsql.c_str(); - ASSERT_EQ(ECSqlStatus::Success, stmt.BindNull(2)) << ecsql.c_str(); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindNull(1)); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindNull(2)); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(3, key.GetInstanceId())); ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); stmt.Finalize(); - ecsql.Sprintf("SELECT p2d, p2d.X, p2d.Y, st.p2d, st.p2d.X, st.p2d.Y FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + SqlPrintfString selectEcsql("SELECT p2d, p2d.X, p2d.Y, st.p2d, st.p2d.X, st.p2d.Y FROM ts.TestClass WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, selectEcsql.GetUtf8CP())) << selectEcsql.GetUtf8CP(); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(1, key.GetInstanceId())); ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); std::set nullItems { "p2d", "p2d.X", "st.p2d", "st.p2d.X" }; @@ -11966,11 +11965,9 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullBinding) //*** Update nested struct to be partially unset { - Utf8String ecsql; - ecsql.Sprintf("INSERT INTO ts.TestClass (st) VALUES (?)"); + SqlPrintfString insertEcsql("INSERT INTO ts.TestClass (st) VALUES (?)"); ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); - + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.GetUtf8CP())) << insertEcsql.GetUtf8CP(); auto& st = stmt.GetBinder(1); st["st"]["b"].BindBoolean(st_st_b); st["st"]["i"].BindBoolean(st_st_i); @@ -11979,19 +11976,20 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullBinding) ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); stmt.Finalize(); - ecsql.Sprintf("UPDATE ONLY ts.TestClass SET st=? WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + SqlPrintfString updateEcsql("UPDATE ONLY ts.TestClass SET st=? WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, updateEcsql.GetUtf8CP())) << updateEcsql.GetUtf8CP(); auto& elementBinder = stmt.GetBinder(1); ASSERT_EQ(ECSqlStatus::Success, elementBinder["st"]["i"].BindNull()); // Set st.st.i = null ASSERT_EQ(ECSqlStatus::Success, elementBinder["st"]["b"].BindBoolean(st_st_b)); // Set st.st.b = true, so that the whole structure doesn't become null + ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(2, key.GetInstanceId())); ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); stmt.Finalize(); - ecsql.Sprintf("SELECT st FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); - - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + SqlPrintfString selectEcsql("SELECT st FROM ts.TestClass WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, selectEcsql.GetUtf8CP())) << selectEcsql.GetUtf8CP(); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(1, key.GetInstanceId())); ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); ASSERT_FALSE(stmt.IsValueNull(0)); @@ -12018,11 +12016,9 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullBinding) //*** Update nested struct to have all properties null { - Utf8String ecsql; - ecsql.Sprintf("INSERT INTO ts.TestClass (st) VALUES (?)"); + SqlPrintfString insertEcsql("INSERT INTO ts.TestClass (st) VALUES (?)"); ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); - + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.GetUtf8CP())) << insertEcsql.GetUtf8CP(); auto& st = stmt.GetBinder(1); st["st"]["i"].BindInt(st_st_i); st["st"]["b"].BindBoolean(st_st_b); @@ -12031,20 +12027,19 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullBinding) ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); stmt.Finalize(); - ecsql.Sprintf("UPDATE ONLY ts.TestClass SET st.p2d=?, st.st.i=?, st.st.b=? " - "WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); - + SqlPrintfString updateEcsql("UPDATE ONLY ts.TestClass SET st.p2d=?, st.st.i=?, st.st.b=? WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, updateEcsql.GetUtf8CP())) << updateEcsql.GetUtf8CP(); ASSERT_EQ(ECSqlStatus::Success, stmt.GetBinder(1).BindPoint2d(p2d)); // Make at least one arg in non-nested st not null. ASSERT_EQ(ECSqlStatus::Success, stmt.GetBinder(2).BindNull()); ASSERT_EQ(ECSqlStatus::Success, stmt.GetBinder(3).BindNull()); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(4, key.GetInstanceId())); ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); stmt.Finalize(); - ecsql.Sprintf("SELECT st FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); - - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + SqlPrintfString selectEcsql("SELECT st FROM ts.TestClass WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, selectEcsql.GetUtf8CP())) << selectEcsql.GetUtf8CP(); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(1, key.GetInstanceId())); ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); ASSERT_FALSE(stmt.IsValueNull(0)); @@ -12064,7 +12059,7 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullBinding) //+---------------+---------------+---------------+---------------+---------------+------ TEST_F(ECSqlStatementTestFixture, UpdateToNullInline) { - ASSERT_EQ(BentleyStatus::SUCCESS, SetupECDb("ec_sql_update_to_null.ecdb", SchemaItem(R"xml( + ASSERT_EQ(BentleyStatus::SUCCESS, SetupECDb("ec_sql_update_to_null_inline.ecdb", SchemaItem(R"xml( @@ -12086,38 +12081,33 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullInline) ///*** Insertable data auto i = 123; auto p2d = DPoint2d::From(23.22, 31.11); - auto st_p2d = DPoint2d::From(53.22, 31.11); - auto st_st_b = true; auto st_st_i = 45; //*** Update all values to null { - Utf8String ecsql; - ecsql.Sprintf("INSERT INTO ts.TestClass (p2d, st, array_i) VALUES (?,?,?)"); + SqlPrintfString insertEcsql("INSERT INTO ts.TestClass (p2d, st, array_i) VALUES (?,?,?)"); ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.GetUtf8CP())) << insertEcsql.GetUtf8CP(); stmt.BindPoint2d(1, p2d); auto& st = stmt.GetBinder(2); - st["p2d"].BindPoint2d(st_p2d); st["st"]["b"].BindBoolean(st_st_b); st["st"]["i"].BindBoolean(st_st_i); - stmt.GetBinder(3).AddArrayElement().BindInt(i); ECInstanceKey key; ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); stmt.Finalize(); - ecsql.Sprintf("UPDATE ONLY ts.TestClass SET p2d=NULL, st=NULL, array_i=NULL WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + SqlPrintfString updateEcsql("UPDATE ONLY ts.TestClass SET p2d=NULL, st=NULL, array_i=NULL WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, updateEcsql.GetUtf8CP())) << updateEcsql.GetUtf8CP(); ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); stmt.Finalize(); - ecsql.Sprintf("SELECT p2d, p2d.X, p2d.Y, st, array_i FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + SqlPrintfString selectEcsql("SELECT p2d, p2d.X, p2d.Y, st, array_i FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, selectEcsql.GetUtf8CP())) << selectEcsql.GetUtf8CP(); ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); for (int i = 0; i < stmt.GetColumnCount(); ++i) { @@ -12140,10 +12130,9 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullInline) // Update points to be partially unset { - Utf8String ecsql; - ecsql.Sprintf("INSERT INTO ts.TestClass (p2d, st) VALUES (?,?)"); + SqlPrintfString insertEcsql("INSERT INTO ts.TestClass (p2d, st) VALUES (?,?)"); ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.GetUtf8CP())) << insertEcsql.GetUtf8CP(); stmt.BindPoint2d(1, p2d); auto& st = stmt.GetBinder(2); st["p2d"].BindPoint2d(st_p2d); @@ -12152,13 +12141,13 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullInline) ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); stmt.Finalize(); - ecsql.Sprintf("UPDATE ONLY ts.TestClass SET p2d.X=NULL, st.p2d.X=NULL WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + SqlPrintfString updateEcsql("UPDATE ONLY ts.TestClass SET p2d.X=NULL, st.p2d.X=NULL WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, updateEcsql.GetUtf8CP())) << updateEcsql.GetUtf8CP(); ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); stmt.Finalize(); - ecsql.Sprintf("SELECT p2d, p2d.X, p2d.Y, st.p2d, st.p2d.X, st.p2d.Y FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + SqlPrintfString selectEcsql("SELECT p2d, p2d.X, p2d.Y, st.p2d, st.p2d.X, st.p2d.Y FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, selectEcsql.GetUtf8CP())) << selectEcsql.GetUtf8CP(); ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); std::set nullItems { "p2d", "p2d.X", "st.p2d", "st.p2d.X" }; @@ -12173,10 +12162,9 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullInline) //*** Update nested struct to be partially unset { - Utf8String ecsql; - ecsql.Sprintf("INSERT INTO ts.TestClass (st) VALUES (?)"); + SqlPrintfString insertEcsql("INSERT INTO ts.TestClass (st) VALUES (?)"); ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.GetUtf8CP())) << insertEcsql.GetUtf8CP(); auto& st = stmt.GetBinder(1); st["st"]["b"].BindBoolean(st_st_b); @@ -12186,14 +12174,13 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullInline) ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); stmt.Finalize(); - ecsql.Sprintf("UPDATE ONLY ts.TestClass SET st.st.i=NULL, st.st.b=TRUE WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + SqlPrintfString updateEcsql("UPDATE ONLY ts.TestClass SET st.st.i=NULL, st.st.b=TRUE WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, updateEcsql.GetUtf8CP())) << updateEcsql.GetUtf8CP(); ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); stmt.Finalize(); - ecsql.Sprintf("SELECT st FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); - - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + SqlPrintfString selectEcsql("SELECT st FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, selectEcsql.GetUtf8CP())) << selectEcsql.GetUtf8CP(); ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); ASSERT_FALSE(stmt.IsValueNull(0)); @@ -12220,10 +12207,9 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullInline) //*** Update nested struct to have all properties null { - Utf8String ecsql; - ecsql.Sprintf("INSERT INTO ts.TestClass (st) VALUES (?)"); + SqlPrintfString insertEcsql("INSERT INTO ts.TestClass (st) VALUES (?)"); ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.GetUtf8CP())) << insertEcsql.GetUtf8CP(); auto& st = stmt.GetBinder(1); st["p2d"].BindPoint2d(p2d); @@ -12234,15 +12220,13 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullInline) ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); stmt.Finalize(); - ecsql.Sprintf("UPDATE ONLY ts.TestClass SET st.st.i=NULL, st.st.b=NULL " - "WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + SqlPrintfString updateEcsql("UPDATE ONLY ts.TestClass SET st.st.i=NULL, st.st.b=NULL WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, updateEcsql.GetUtf8CP())) << updateEcsql.GetUtf8CP(); ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); stmt.Finalize(); - ecsql.Sprintf("SELECT st FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); - - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, ecsql.c_str())) << ecsql.c_str(); + SqlPrintfString selectEcsql("SELECT st FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, selectEcsql.GetUtf8CP())) << selectEcsql.GetUtf8CP(); ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); ASSERT_FALSE(stmt.IsValueNull(0)); diff --git a/iModelCore/ECDb/Tests/NonPublished/JsonUpdaterTests.cpp b/iModelCore/ECDb/Tests/NonPublished/JsonUpdaterTests.cpp index d3f4b85ee3b..7016136d089 100644 --- a/iModelCore/ECDb/Tests/NonPublished/JsonUpdaterTests.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/JsonUpdaterTests.cpp @@ -1047,17 +1047,22 @@ TEST_F(JsonUpdaterTests, UpdateStructPropertyToNull) ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)); } - ECClassCP testClass = m_ecdb.Schemas().GetClass("TestSchema", "TestClass"); - ASSERT_TRUE(testClass != nullptr); - EXPECT_EQ(JsonValue("[{\"IntProp\":15,\"ClassProp\":{\"MyStructNumber\":17}}]"), GetHelper().ExecuteSelectECSql(Utf8PrintfString("SELECT IntProp,ClassProp FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()).c_str())); + // Check for correct ClassProp value to be stored + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT IntProp,ClassProp FROM ts.TestClass WHERE ECInstanceId=?")); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(1, key.GetInstanceId())); + EXPECT_EQ(JsonValue("[{\"IntProp\":15,\"ClassProp\":{\"MyStructNumber\":17}}]"), GetHelper().ExecutePreparedECSql(stmt)); // Update test instance + ECClassCP testClass = m_ecdb.Schemas().GetClass("TestSchema", "TestClass"); + ASSERT_TRUE(testClass != nullptr); JsonUpdater updater(m_ecdb, *testClass, nullptr); ASSERT_TRUE(updater.IsValid()); ASSERT_EQ(BE_SQLITE_OK, updater.Update(key.GetInstanceId(), JsonValue("{\"IntProp\": 6, \"ClassProp\": null}").m_value)); // Check for ClassProp to not exist (be null) - EXPECT_EQ(JsonValue("[{\"IntProp\":6}]"), GetHelper().ExecuteSelectECSql(Utf8PrintfString("SELECT IntProp,ClassProp FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()).c_str())); + stmt.Reset(); + EXPECT_EQ(JsonValue("[{\"IntProp\":6}]"), GetHelper().ExecutePreparedECSql(stmt)); } //--------------------------------------------------------------------------------------- @@ -1091,17 +1096,22 @@ TEST_F(JsonUpdaterTests, UpdateArrayPropertyToNull) ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)); } - ECClassCP testClass = m_ecdb.Schemas().GetClass("TestSchema", "TestClass"); - ASSERT_TRUE(testClass != nullptr); - EXPECT_EQ(JsonValue("[{\"IntProp\":15,\"ArrBoolProp\":[true, false],\"ArrStructProp\":[{\"DeepNumber\":17},{\"DeepNumber\":5},{\"DeepNumber\":12}]}]"), GetHelper().ExecuteSelectECSql(Utf8PrintfString("SELECT IntProp,ArrBoolProp,ArrStructProp FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()).c_str())); + // Check for correct ArrBoolProp and ArrStructProp values to be stored + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT IntProp,ArrBoolProp,ArrStructProp FROM ts.TestClass WHERE ECInstanceId=?")); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(1, key.GetInstanceId())); + EXPECT_EQ(JsonValue("[{\"IntProp\":15,\"ArrBoolProp\":[true, false],\"ArrStructProp\":[{\"DeepNumber\":17},{\"DeepNumber\":5},{\"DeepNumber\":12}]}]"), GetHelper().ExecutePreparedECSql(stmt)); // Update test instance + ECClassCP testClass = m_ecdb.Schemas().GetClass("TestSchema", "TestClass"); + ASSERT_TRUE(testClass != nullptr); JsonUpdater updater(m_ecdb, *testClass, nullptr); ASSERT_TRUE(updater.IsValid()); ASSERT_EQ(BE_SQLITE_OK, updater.Update(key.GetInstanceId(), JsonValue("{\"IntProp\":6,\"ArrBoolProp\": null, \"ArrStructProp\": null}").m_value)); // Check for ArrBoolProp and ArrStructProp to not exist (be null) - EXPECT_EQ(JsonValue("[{\"IntProp\":6}]"), GetHelper().ExecuteSelectECSql(Utf8PrintfString("SELECT IntProp,ArrBoolProp,ArrStructProp FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()).c_str())); + stmt.Reset(); + EXPECT_EQ(JsonValue("[{\"IntProp\":6}]"), GetHelper().ExecutePreparedECSql(stmt)); } END_ECDBUNITTESTS_NAMESPACE From a998ae15474b69decaf9fc89c0e4dd8c219ef074 Mon Sep 17 00:00:00 2001 From: "Sarunas.Griskus" Date: Thu, 4 Jul 2024 11:46:43 +0300 Subject: [PATCH 15/15] Only use SqlPrintfString for string interpolation purposes --- .../NonPublished/ECSqlStatementTests.cpp | 76 +++++++++---------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/iModelCore/ECDb/Tests/NonPublished/ECSqlStatementTests.cpp b/iModelCore/ECDb/Tests/NonPublished/ECSqlStatementTests.cpp index a5afbeb212b..de9b02ed7a6 100644 --- a/iModelCore/ECDb/Tests/NonPublished/ECSqlStatementTests.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/ECSqlStatementTests.cpp @@ -11834,9 +11834,9 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullBinding) //*** Update all values to null { - SqlPrintfString insertEcsql("INSERT INTO ts.TestClass (p2d, st, array_i) VALUES (?,?,?)"); + Utf8String insertEcsql("INSERT INTO ts.TestClass (p2d, st, array_i) VALUES (?,?,?)"); ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.GetUtf8CP())) << insertEcsql.GetUtf8CP(); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.c_str())) << insertEcsql.c_str(); stmt.BindPoint2d(1, p2d); auto& st = stmt.GetBinder(2); st["p2d"].BindPoint2d(st_p2d); @@ -11848,8 +11848,8 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullBinding) ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); stmt.Finalize(); - SqlPrintfString updateEcsql("UPDATE ONLY ts.TestClass SET p2d=?, st=?, array_i=? WHERE ECInstanceId=?", key.GetInstanceId().ToString().c_str()); - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, updateEcsql.GetUtf8CP())) << updateEcsql.GetUtf8CP(); + Utf8String updateEcsql("UPDATE ONLY ts.TestClass SET p2d=?, st=?, array_i=? WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, updateEcsql.c_str())) << updateEcsql.c_str(); for (int i = 1; i <= 3; ++i) { ASSERT_EQ(ECSqlStatus::Success, stmt.BindNull(i)); @@ -11859,8 +11859,8 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullBinding) ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); stmt.Finalize(); - SqlPrintfString selectEcsql("SELECT p2d, p2d.X, p2d.Y, st, array_i FROM ts.TestClass WHERE ECInstanceId=?"); - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, selectEcsql.GetUtf8CP())) << selectEcsql.GetUtf8CP(); + Utf8String selectEcsql("SELECT p2d, p2d.X, p2d.Y, st, array_i FROM ts.TestClass WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, selectEcsql.c_str())) << selectEcsql.c_str(); ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(1, key.GetInstanceId())); ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); for (int i = 0; i < stmt.GetColumnCount(); ++i) @@ -11884,17 +11884,17 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullBinding) //*** Update array to contain two null elements { - SqlPrintfString insertEcsql("INSERT INTO ts.TestClass (array_i) VALUES (?)"); + Utf8String insertEcsql("INSERT INTO ts.TestClass (array_i) VALUES (?)"); ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.GetUtf8CP())) << insertEcsql.GetUtf8CP(); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.c_str())) << insertEcsql.c_str(); stmt.GetBinder(1).AddArrayElement().BindInt(i); ECInstanceKey key; ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); stmt.Finalize(); - SqlPrintfString updateEcsql("UPDATE ONLY ts.TestClass SET array_i=? WHERE ECInstanceId=?"); - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, updateEcsql.GetUtf8CP())) << updateEcsql.GetUtf8CP(); + Utf8String updateEcsql("UPDATE ONLY ts.TestClass SET array_i=? WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, updateEcsql.c_str())) << updateEcsql.c_str(); IECSqlBinder& arrayBinder = stmt.GetBinder(1); ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindNull()); ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindNull()); @@ -11903,8 +11903,8 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullBinding) ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); stmt.Finalize(); - SqlPrintfString selectEcsql("SELECT array_i FROM ts.TestClass WHERE ECInstanceId=?"); - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, selectEcsql.GetUtf8CP())) << selectEcsql.GetUtf8CP(); + Utf8String selectEcsql("SELECT array_i FROM ts.TestClass WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, selectEcsql.c_str())) << selectEcsql.c_str(); ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(1, key.GetInstanceId())); ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); @@ -11927,9 +11927,9 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullBinding) // Update points to be partially unset { - SqlPrintfString insertEcsql("INSERT INTO ts.TestClass (p2d, st) VALUES (?,?)"); + Utf8String insertEcsql("INSERT INTO ts.TestClass (p2d, st) VALUES (?,?)"); ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.GetUtf8CP())) << insertEcsql.GetUtf8CP(); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.c_str())) << insertEcsql.c_str(); stmt.BindPoint2d(1, p2d); auto& st = stmt.GetBinder(2); st["p2d"].BindPoint2d(st_p2d); @@ -11938,8 +11938,8 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullBinding) ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); stmt.Finalize(); - SqlPrintfString updateEcsql("UPDATE ONLY ts.TestClass SET p2d.X=?, st.p2d.X=? WHERE ECInstanceId=?"); - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, updateEcsql.GetUtf8CP())) << updateEcsql.GetUtf8CP(); + Utf8String updateEcsql("UPDATE ONLY ts.TestClass SET p2d.X=?, st.p2d.X=? WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, updateEcsql.c_str())) << updateEcsql.c_str(); ASSERT_EQ(ECSqlStatus::Success, stmt.BindNull(1)); ASSERT_EQ(ECSqlStatus::Success, stmt.BindNull(2)); @@ -11948,8 +11948,8 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullBinding) ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); stmt.Finalize(); - SqlPrintfString selectEcsql("SELECT p2d, p2d.X, p2d.Y, st.p2d, st.p2d.X, st.p2d.Y FROM ts.TestClass WHERE ECInstanceId=?"); - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, selectEcsql.GetUtf8CP())) << selectEcsql.GetUtf8CP(); + Utf8String selectEcsql("SELECT p2d, p2d.X, p2d.Y, st.p2d, st.p2d.X, st.p2d.Y FROM ts.TestClass WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, selectEcsql.c_str())) << selectEcsql.c_str(); ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(1, key.GetInstanceId())); ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); @@ -11965,9 +11965,9 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullBinding) //*** Update nested struct to be partially unset { - SqlPrintfString insertEcsql("INSERT INTO ts.TestClass (st) VALUES (?)"); + Utf8String insertEcsql("INSERT INTO ts.TestClass (st) VALUES (?)"); ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.GetUtf8CP())) << insertEcsql.GetUtf8CP(); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.c_str())) << insertEcsql.c_str(); auto& st = stmt.GetBinder(1); st["st"]["b"].BindBoolean(st_st_b); st["st"]["i"].BindBoolean(st_st_i); @@ -11976,8 +11976,8 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullBinding) ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); stmt.Finalize(); - SqlPrintfString updateEcsql("UPDATE ONLY ts.TestClass SET st=? WHERE ECInstanceId=?"); - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, updateEcsql.GetUtf8CP())) << updateEcsql.GetUtf8CP(); + Utf8String updateEcsql("UPDATE ONLY ts.TestClass SET st=? WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, updateEcsql.c_str())) << updateEcsql.c_str(); auto& elementBinder = stmt.GetBinder(1); ASSERT_EQ(ECSqlStatus::Success, elementBinder["st"]["i"].BindNull()); // Set st.st.i = null @@ -11987,8 +11987,8 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullBinding) ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); stmt.Finalize(); - SqlPrintfString selectEcsql("SELECT st FROM ts.TestClass WHERE ECInstanceId=?"); - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, selectEcsql.GetUtf8CP())) << selectEcsql.GetUtf8CP(); + Utf8String selectEcsql("SELECT st FROM ts.TestClass WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, selectEcsql.c_str())) << selectEcsql.c_str(); ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(1, key.GetInstanceId())); ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); @@ -12016,9 +12016,9 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullBinding) //*** Update nested struct to have all properties null { - SqlPrintfString insertEcsql("INSERT INTO ts.TestClass (st) VALUES (?)"); + Utf8String insertEcsql("INSERT INTO ts.TestClass (st) VALUES (?)"); ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.GetUtf8CP())) << insertEcsql.GetUtf8CP(); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.c_str())) << insertEcsql.c_str(); auto& st = stmt.GetBinder(1); st["st"]["i"].BindInt(st_st_i); st["st"]["b"].BindBoolean(st_st_b); @@ -12027,8 +12027,8 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullBinding) ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); stmt.Finalize(); - SqlPrintfString updateEcsql("UPDATE ONLY ts.TestClass SET st.p2d=?, st.st.i=?, st.st.b=? WHERE ECInstanceId=?"); - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, updateEcsql.GetUtf8CP())) << updateEcsql.GetUtf8CP(); + Utf8String updateEcsql("UPDATE ONLY ts.TestClass SET st.p2d=?, st.st.i=?, st.st.b=? WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, updateEcsql.c_str())) << updateEcsql.c_str(); ASSERT_EQ(ECSqlStatus::Success, stmt.GetBinder(1).BindPoint2d(p2d)); // Make at least one arg in non-nested st not null. ASSERT_EQ(ECSqlStatus::Success, stmt.GetBinder(2).BindNull()); ASSERT_EQ(ECSqlStatus::Success, stmt.GetBinder(3).BindNull()); @@ -12037,8 +12037,8 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullBinding) ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); stmt.Finalize(); - SqlPrintfString selectEcsql("SELECT st FROM ts.TestClass WHERE ECInstanceId=?"); - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, selectEcsql.GetUtf8CP())) << selectEcsql.GetUtf8CP(); + Utf8String selectEcsql("SELECT st FROM ts.TestClass WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, selectEcsql.c_str())) << selectEcsql.c_str(); ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(1, key.GetInstanceId())); ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); @@ -12087,9 +12087,9 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullInline) //*** Update all values to null { - SqlPrintfString insertEcsql("INSERT INTO ts.TestClass (p2d, st, array_i) VALUES (?,?,?)"); + Utf8String insertEcsql("INSERT INTO ts.TestClass (p2d, st, array_i) VALUES (?,?,?)"); ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.GetUtf8CP())) << insertEcsql.GetUtf8CP(); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.c_str())) << insertEcsql.c_str(); stmt.BindPoint2d(1, p2d); auto& st = stmt.GetBinder(2); st["p2d"].BindPoint2d(st_p2d); @@ -12130,9 +12130,9 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullInline) // Update points to be partially unset { - SqlPrintfString insertEcsql("INSERT INTO ts.TestClass (p2d, st) VALUES (?,?)"); + Utf8String insertEcsql("INSERT INTO ts.TestClass (p2d, st) VALUES (?,?)"); ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.GetUtf8CP())) << insertEcsql.GetUtf8CP(); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.c_str())) << insertEcsql.c_str(); stmt.BindPoint2d(1, p2d); auto& st = stmt.GetBinder(2); st["p2d"].BindPoint2d(st_p2d); @@ -12162,9 +12162,9 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullInline) //*** Update nested struct to be partially unset { - SqlPrintfString insertEcsql("INSERT INTO ts.TestClass (st) VALUES (?)"); + Utf8String insertEcsql("INSERT INTO ts.TestClass (st) VALUES (?)"); ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.GetUtf8CP())) << insertEcsql.GetUtf8CP(); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.c_str())) << insertEcsql.c_str(); auto& st = stmt.GetBinder(1); st["st"]["b"].BindBoolean(st_st_b); @@ -12207,9 +12207,9 @@ TEST_F(ECSqlStatementTestFixture, UpdateToNullInline) //*** Update nested struct to have all properties null { - SqlPrintfString insertEcsql("INSERT INTO ts.TestClass (st) VALUES (?)"); + Utf8String insertEcsql("INSERT INTO ts.TestClass (st) VALUES (?)"); ECSqlStatement stmt; - ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.GetUtf8CP())) << insertEcsql.GetUtf8CP(); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.c_str())) << insertEcsql.c_str(); auto& st = stmt.GetBinder(1); st["p2d"].BindPoint2d(p2d);