Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions query/ir/interpreter/NLTranslator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,12 @@ bool isConstantLike(mlir::Value value) {
[](mlir::Value operand) { return isConstantLike(operand); });
}

// A predicate or a NOT over constants folds to a ColumnConst just as an arithmetic
// operation does, which the column answers and isConstantLike, reading the op, does not.
bool isConstantColumn(const Column* column) {
return column->getContainerKind() == ContainerKind::code<ColumnConst>();
}

// A drain wires its emit loop's variables into the accumulator's one output slot per
// column, so an accumulator can serve exactly one nl.collect or nl.unwind_collect. A
// second drain would rebind the first's outputs to its own, incompatible column types
Expand Down Expand Up @@ -1220,7 +1226,7 @@ void NLTranslator::addTruncateColumn(mlir::Value inputValue,
Column* output = nullptr;
NLBroadcastFunction copyPrefix = nullptr;

if (isConstantLike(inputValue)) {
if (isConstantColumn(input)) {
output = _memory->allocSame(input);
copyPrefix = NLExecutor::selectConstBlockRepeatFunction();
} else if (const auto nullableType = mlir::dyn_cast<storage::NullableType>(elementType)) {
Expand Down Expand Up @@ -1313,7 +1319,7 @@ void NLTranslator::addSkipColumn(mlir::Value inputValue,
Column* output = nullptr;
NLCopyFunction copySuffix = nullptr;

if (isConstantLike(inputValue)) {
if (isConstantColumn(input)) {
output = _memory->allocSame(input);
copySuffix = NLExecutor::selectConstCopyFunction();
} else if (const auto nullableType = mlir::dyn_cast<storage::NullableType>(elementType)) {
Expand Down
10 changes: 10 additions & 0 deletions test/query/ir/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -479,3 +479,13 @@ target_link_libraries(test_query_ir_order_by_aggregate_unprojected_key PRIVATE
turing_db_storage_s
MLIRParser)
target_include_directories(test_query_ir_order_by_aggregate_unprojected_key PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})

add_turing_test(test_query_ir_constant_predicate_projection ConstantPredicateProjectionTest.cpp)
target_link_libraries(test_query_ir_constant_predicate_projection PRIVATE
turing_db_s
turing_testenv_s
turing_db_examples_s
turing_db_system_s
turing_db_storage_s
turing_db_interpreter_v3_s)
target_include_directories(test_query_ir_constant_predicate_projection PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
156 changes: 156 additions & 0 deletions test/query/ir/ConstantPredicateProjectionTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
#include <gtest/gtest.h>

#include <memory>
#include <span>
#include <string>
#include <string_view>
#include <vector>

#include "NLOutputSink.h"
#include "QueryInterpreterV3.h"
#include "QueryStatus.h"

#include "Graph.h"
#include "SimpleGraph.h"
#include "SystemAccessor.h"
#include "SystemManager.h"
#include "columns/ColumnConst.h"
#include "columns/ColumnMask.h"
#include "metadata/PropertyType.h"
#include "versioning/ChangeID.h"
#include "versioning/CommitHash.h"

#include "TuringException.h"
#include "TuringTest.h"
#include "TuringTestEnv.h"

using namespace db;
using namespace turing::test;

namespace {

using BoolRow = std::vector<bool>;
using BoolRows = std::vector<BoolRow>;

// A predicate over constants alone is the one value it computes, so it reaches the sink as
// a ColumnConst standing for every row, where a predicate evaluated per row comes as a mask.
bool readBool(const Column* column, size_t row) {
if (const auto* constants = dynamic_cast<const ColumnConst<CustomBool>*>(column)) {
return static_cast<bool>((*constants)[row]);
} else if (const auto* mask = dynamic_cast<const ColumnMask*>(column)) {
return (*mask)[row];
}

throw TuringException("ConstantPredicateProjectionTest: unsupported output column type");
}

class CollectingBoolSink : public NLOutputSink {
public:
void appendChunks(std::span<const Column* const> chunks, size_t offset, size_t rowCount) override {
for (size_t rowIndex = offset; rowIndex < offset + rowCount; rowIndex++) {
BoolRow& row = _rows.emplace_back();
for (const Column* column : chunks) {
row.push_back(readBool(column, rowIndex));
}
}
}

const BoolRows& getRows() const { return _rows; }

private:
BoolRows _rows;
};

}

// A projection of a boolean expression over constants alone: the comparison and the
// conjunction are computed once, and the window SKIP 0 LIMIT 1 keeps the single row they
// stand for.
class ConstantPredicateProjectionTest : public TuringTest {
protected:
void initialize() override {
_env = TuringTestEnv::create(fs::Path {_outDir} / "turing");
_interpreter = std::make_unique<QueryInterpreterV3>(&_env->getSystemManager());

SystemAccessor system = _env->getSystemManager().accessUnique();
Graph* graph = system.createGraph(_graphName);
SimpleGraph::createSimpleGraph(graph);
}

void expectRows(std::string_view query, const BoolRows& expected) {
CollectingBoolSink sink;

QueryStatus status;
_interpreter->execute(status,
query,
_graphName,
CommitHash::head(),
ChangeID::head(),
&_env->getMem(),
&sink);

ASSERT_TRUE(status.isOk()) << "query: " << query << "\nerror: " << status.getError();

EXPECT_EQ(sink.getRows(), expected) << "query: " << query;
}

const std::string _graphName = "simpledb";
std::unique_ptr<TuringTestEnv> _env;
std::unique_ptr<QueryInterpreterV3> _interpreter;
};

TEST_F(ConstantPredicateProjectionTest, emitsAConstantComparison) {
const BoolRows expected = {{true}};
expectRows("RETURN 1 < 2", expected);
}

TEST_F(ConstantPredicateProjectionTest, emitsAConstantConjunction) {
const BoolRows expected = {{false}};
expectRows("RETURN true and false", expected);
}

TEST_F(ConstantPredicateProjectionTest, emitsAConstantComparisonUnderAWindow) {
const BoolRows expected = {{true}};
expectRows("RETURN 1 < 2 SKIP 0 LIMIT 1", expected);
}

TEST_F(ConstantPredicateProjectionTest, emitsAConstantConjunctionUnderAWindow) {
const BoolRows expected = {{false}};
expectRows("RETURN true and false SKIP 0 LIMIT 1", expected);
}

TEST_F(ConstantPredicateProjectionTest, emitsAConstantNegationUnderAWindow) {
const BoolRows expected = {{false}};
expectRows("RETURN not true SKIP 0 LIMIT 1", expected);
}

TEST_F(ConstantPredicateProjectionTest, emitsAConstantComparisonUnderALimit) {
const BoolRows expected = {{true}};
expectRows("RETURN 1 < 2 LIMIT 1", expected);
}

TEST_F(ConstantPredicateProjectionTest, emitsAConstantComparisonUnderASkip) {
const BoolRows expected = {{true}};
expectRows("RETURN 1 < 2 SKIP 0", expected);
}

TEST_F(ConstantPredicateProjectionTest, emitsNothingWhenTheSkipPassesTheOnlyRow) {
expectRows("RETURN 1 < 2 SKIP 1", {});
}

TEST_F(ConstantPredicateProjectionTest, emitsNothingWhenTheWindowSkipsTheOnlyRow) {
expectRows("RETURN 1 < 2 SKIP 1 LIMIT 1", {});
}

TEST_F(ConstantPredicateProjectionTest, emitsNothingWhenTheLimitIsZero) {
expectRows("RETURN true and false SKIP 0 LIMIT 0", {});
}

TEST_F(ConstantPredicateProjectionTest, emitsEveryConstantPredicateColumnOfTheRow) {
const BoolRows expected = {{true, false}};
expectRows("RETURN 1 < 2, true and false SKIP 0 LIMIT 1", expected);
}

int main(int argc, char** argv) {
return turing::test::turingTestMain(argc, argv);
}
Loading