Skip to content

Commit

Permalink
don't update indexes when all input columns are specified for indexes…
Browse files Browse the repository at this point in the history
… but values are not changed (#12940)
  • Loading branch information
gridnevvvit authored Jan 21, 2025
1 parent 61d9766 commit caa2332
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 12 deletions.
12 changes: 0 additions & 12 deletions ydb/core/kqp/opt/physical/effects/kqp_opt_phy_upsert_index.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,18 +153,6 @@ TExprBase MakeUpsertIndexRows(TKqpPhyUpsertIndexMode mode, const TDqPhyPrecomput
const TVector<TStringBuf>& indexColumns, const TKikimrTableDescription& table, TPositionHandle pos,
TExprContext& ctx, bool opt)
{
// Check if we can update index table from just input data
bool allColumnFromInput = true; // - indicate all data from input
for (const auto& column : indexColumns) {
allColumnFromInput = allColumnFromInput && inputColumns.contains(column);
}

if (allColumnFromInput) {
return mode == TKqpPhyUpsertIndexMode::UpdateOn
? MakeNonexistingRowsFilter(inputRows, lookupDict, table.Metadata->KeyColumnNames, pos, ctx)
: TExprBase(inputRows);
}

auto inputRowsArg = TCoArgument(ctx.NewArgument(pos, "input_rows"));
auto inputRowArg = TCoArgument(ctx.NewArgument(pos, "input_row"));
auto lookupDictArg = TCoArgument(ctx.NewArgument(pos, "lookup_dict"));
Expand Down
164 changes: 164 additions & 0 deletions ydb/core/kqp/ut/service/kqp_qs_queries_ut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,170 @@ Y_UNIT_TEST_SUITE(KqpQueryService) {
UNIT_ASSERT_VALUES_EQUAL(count, 1);
}

Y_UNIT_TEST(ExecuteQueryUpsertDoesntChangeIndexedValuesIfNotChanged) {
auto kikimr = DefaultKikimrRunner();
auto db = kikimr.GetQueryClient();

auto settings = TExecuteQuerySettings()
.StatsMode(EStatsMode::Full);

{
auto result = db.ExecuteQuery(R"(
create table test (
id1 Uint64,
id2 Uint64,
value Utf8,
PRIMARY KEY(id1, id2),
INDEX Id2Idx GLOBAL ON (id2),
INDEX ValueIdx GLOBAL ON (value)
);
)", TTxControl::NoTx()).ExtractValueSync();
UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString());
}

{
auto result = db.ExecuteQuery(R"(
UPSERT INTO test (id1, id2, value) VALUES (2, 2, "c"u), (3, 3, null), (4, 4, null);
)", TTxControl::BeginTx().CommitTx(), settings).ExtractValueSync();
UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString());
auto stats = NYdb::TProtoAccessor::GetProto(*result.GetStats());
AssertTableStats(stats, "/Root/test", {.ExpectedUpdates = 3});
AssertTableStats(stats, "/Root/test/Id2Idx/indexImplTable", {.ExpectedUpdates = 3});
AssertTableStats(stats, "/Root/test/ValueIdx/indexImplTable", {.ExpectedUpdates = 3});
}

// value not changed, no updates to indexes
{
auto result = db.ExecuteQuery(R"(
UPSERT INTO test (id1, id2) VALUES (2, 2);
)", TTxControl::BeginTx().CommitTx(), settings).ExtractValueSync();
UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString());
auto stats = NYdb::TProtoAccessor::GetProto(*result.GetStats());
AssertTableStats(stats, "/Root/test", {.ExpectedUpdates = 1});
AssertTableStats(stats, "/Root/test/Id2Idx/indexImplTable", {.ExpectedUpdates = 0});
AssertTableStats(stats, "/Root/test/ValueIdx/indexImplTable", {.ExpectedUpdates = 0});
}

// value not changed, no updates to indexes
{
auto result = db.ExecuteQuery(R"(
UPSERT INTO test (id1, id2, value) VALUES (2, 2, "c"u);
)", TTxControl::BeginTx().CommitTx(), settings).ExtractValueSync();
UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString());
auto stats = NYdb::TProtoAccessor::GetProto(*result.GetStats());
AssertTableStats(stats, "/Root/test", {.ExpectedUpdates = 1});
AssertTableStats(stats, "/Root/test/Id2Idx/indexImplTable", {.ExpectedUpdates = 0});
AssertTableStats(stats, "/Root/test/ValueIdx/indexImplTable", {.ExpectedUpdates = 0});
}

// value IS changed, updates to index by VALUE
{
auto result = db.ExecuteQuery(R"(
UPSERT INTO test (id1, id2, value) VALUES (2, 2, "Q"u);
)", TTxControl::BeginTx().CommitTx(), settings).ExtractValueSync();
UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString());
auto stats = NYdb::TProtoAccessor::GetProto(*result.GetStats());
AssertTableStats(stats, "/Root/test", {.ExpectedUpdates = 1});
AssertTableStats(stats, "/Root/test/Id2Idx/indexImplTable", {.ExpectedUpdates = 0});
AssertTableStats(stats, "/Root/test/ValueIdx/indexImplTable", {.ExpectedUpdates = 1});
const TString query = "SELECT id1, id2, value FROM test VIEW ValueIdx WHERE value = \"Q\"u";
auto selectResult = db.ExecuteQuery(query, TTxControl::BeginTx().CommitTx(), settings).ExtractValueSync();
CompareYson(R"([[[2u];[2u];["Q"]]])", FormatResultSetYson(selectResult.GetResultSet(0)));
}

// value IS NOT changed
{
auto result = db.ExecuteQuery(R"(
UPDATE test ON SELECT 2 as id1, 2 as id2, "Q"u as value;
)", TTxControl::BeginTx().CommitTx(), settings).ExtractValueSync();
UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString());
auto stats = NYdb::TProtoAccessor::GetProto(*result.GetStats());
AssertTableStats(stats, "/Root/test", {.ExpectedUpdates = 1});
AssertTableStats(stats, "/Root/test/Id2Idx/indexImplTable", {.ExpectedUpdates = 0});
AssertTableStats(stats, "/Root/test/ValueIdx/indexImplTable", {.ExpectedUpdates = 0});
const TString query = "SELECT id1, id2, value FROM test VIEW ValueIdx WHERE value = \"Q\"u";
auto selectResult = db.ExecuteQuery(query, TTxControl::BeginTx().CommitTx(), settings).ExtractValueSync();
CompareYson(R"([[[2u];[2u];["Q"]]])", FormatResultSetYson(selectResult.GetResultSet(0)));
}


// value IS NOT changed
{
auto result = db.ExecuteQuery(R"(
UPDATE test ON SELECT 2 as id1, 2 as id2, "R"u as value;
)", TTxControl::BeginTx().CommitTx(), settings).ExtractValueSync();
UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString());
auto stats = NYdb::TProtoAccessor::GetProto(*result.GetStats());
AssertTableStats(stats, "/Root/test", {.ExpectedUpdates = 1});
AssertTableStats(stats, "/Root/test/Id2Idx/indexImplTable", {.ExpectedUpdates = 0});
AssertTableStats(stats, "/Root/test/ValueIdx/indexImplTable", {.ExpectedUpdates = 1});
const TString query = "SELECT id1, id2, value FROM test VIEW ValueIdx WHERE value = \"R\"u";
auto selectResult = db.ExecuteQuery(query, TTxControl::BeginTx().CommitTx(), settings).ExtractValueSync();
CompareYson(R"([[[2u];[2u];["R"]]])", FormatResultSetYson(selectResult.GetResultSet(0)));
}

// value (null) IS NOT changed
{
auto result = db.ExecuteQuery(R"(
UPSERT INTO test (id1, id2, value) VALUES (3, 3, null);
)", TTxControl::BeginTx().CommitTx(), settings).ExtractValueSync();
UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString());
auto stats = NYdb::TProtoAccessor::GetProto(*result.GetStats());
AssertTableStats(stats, "/Root/test", {.ExpectedUpdates = 1});
AssertTableStats(stats, "/Root/test/Id2Idx/indexImplTable", {.ExpectedUpdates = 0});
AssertTableStats(stats, "/Root/test/ValueIdx/indexImplTable", {.ExpectedUpdates = 0});
const TString query = "SELECT id1, id2, value FROM test VIEW ValueIdx WHERE value is null";
auto selectResult = db.ExecuteQuery(query, TTxControl::BeginTx().CommitTx(), settings).ExtractValueSync();
CompareYson(R"([[[3u];[3u];#];[[4u];[4u];#]])", FormatResultSetYson(selectResult.GetResultSet(0)));
}

// value (null) IS NOT changed
{
auto result = db.ExecuteQuery(R"(
UPDATE test ON SELECT 3 as id1, 3 as id2, null as value;
)", TTxControl::BeginTx().CommitTx(), settings).ExtractValueSync();
UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString());
auto stats = NYdb::TProtoAccessor::GetProto(*result.GetStats());
AssertTableStats(stats, "/Root/test", {.ExpectedUpdates = 1});
AssertTableStats(stats, "/Root/test/Id2Idx/indexImplTable", {.ExpectedUpdates = 0});
AssertTableStats(stats, "/Root/test/ValueIdx/indexImplTable", {.ExpectedUpdates = 0});
const TString query = "SELECT id1, id2, value FROM test VIEW ValueIdx WHERE value is null";
auto selectResult = db.ExecuteQuery(query, TTxControl::BeginTx().CommitTx(), settings).ExtractValueSync();
CompareYson(R"([[[3u];[3u];#];[[4u];[4u];#]])", FormatResultSetYson(selectResult.GetResultSet(0)));
}

// value (null) IS changed
{
auto result = db.ExecuteQuery(R"(
UPSERT INTO test (id1, id2, value) VALUES (3, 3, "T"u);
)", TTxControl::BeginTx().CommitTx(), settings).ExtractValueSync();
UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString());
auto stats = NYdb::TProtoAccessor::GetProto(*result.GetStats());
AssertTableStats(stats, "/Root/test", {.ExpectedUpdates = 1});
AssertTableStats(stats, "/Root/test/Id2Idx/indexImplTable", {.ExpectedUpdates = 0});
AssertTableStats(stats, "/Root/test/ValueIdx/indexImplTable", {.ExpectedUpdates = 1});
const TString query = "SELECT id1, id2, value FROM test VIEW ValueIdx WHERE value = \"T\"u";
auto selectResult = db.ExecuteQuery(query, TTxControl::BeginTx().CommitTx(), settings).ExtractValueSync();
CompareYson(R"([[[3u];[3u];["T"]]])", FormatResultSetYson(selectResult.GetResultSet(0)));
}

// value (null) IS changed
{
auto result = db.ExecuteQuery(R"(
UPDATE test ON SELECT 4 as id1, 4 as id2, "L"u as value;
)", TTxControl::BeginTx().CommitTx(), settings).ExtractValueSync();
UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString());
auto stats = NYdb::TProtoAccessor::GetProto(*result.GetStats());
AssertTableStats(stats, "/Root/test", {.ExpectedUpdates = 1});
AssertTableStats(stats, "/Root/test/Id2Idx/indexImplTable", {.ExpectedUpdates = 0});
AssertTableStats(stats, "/Root/test/ValueIdx/indexImplTable", {.ExpectedUpdates = 1});
const TString query = "SELECT id1, id2, value FROM test VIEW ValueIdx WHERE value = \"L\"u";
auto selectResult = db.ExecuteQuery(query, TTxControl::BeginTx().CommitTx(), settings).ExtractValueSync();
CompareYson(R"([[[4u];[4u];["L"]]])", FormatResultSetYson(selectResult.GetResultSet(0)));
}
}

Y_UNIT_TEST(ExecuteQueryPure) {
auto kikimr = DefaultKikimrRunner();
auto db = kikimr.GetQueryClient();
Expand Down

0 comments on commit caa2332

Please sign in to comment.