From 90d17f1bfdb11a85fcdd8f6eb7a15da42669ad9f Mon Sep 17 00:00:00 2001 From: Josip Mrden Date: Fri, 5 May 2023 15:34:25 +0200 Subject: [PATCH] Merge from match + load csv invalid behaviour --- src/query/plan/operator.cpp | 22 ++++++++++----------- src/query/plan/pretty_print.cpp | 26 ++++++++++++++++++++----- src/query/plan/rewrite/index_lookup.hpp | 10 ++++++++++ src/utils/csv_parsing.cpp | 2 +- src/utils/csv_parsing.hpp | 2 +- tests/unit/utils_csv_parsing.cpp | 2 +- 6 files changed, 45 insertions(+), 19 deletions(-) diff --git a/src/query/plan/operator.cpp b/src/query/plan/operator.cpp index fb729e392..dbe3fe0f0 100644 --- a/src/query/plan/operator.cpp +++ b/src/query/plan/operator.cpp @@ -4598,7 +4598,7 @@ LoadCsv::LoadCsv(std::shared_ptr input, Expression *file, bool MG_ASSERT(file_, "Something went wrong - '{}' member file_ shouldn't be a nullptr", __func__); } -bool LoadCsv::Accept(HierarchicalLogicalOperatorVisitor &visitor) { return false; }; +ACCEPT_WITH_INPUT(LoadCsv) class LoadCsvCursor; @@ -4649,14 +4649,12 @@ TypedValue CsvRowToTypedMap(csv::Reader::Row &row, csv::Reader::Header header) { class LoadCsvCursor : public Cursor { const LoadCsv *self_; const UniqueCursorPtr input_cursor_; - bool input_is_once_; + bool did_pull_; std::optional reader_{}; public: LoadCsvCursor(const LoadCsv *self, utils::MemoryResource *mem) - : self_(self), input_cursor_(self_->input_->MakeCursor(mem)) { - input_is_once_ = dynamic_cast(self_->input_.get()); - } + : self_(self), input_cursor_(self_->input_->MakeCursor(mem)), did_pull_{false} {} bool Pull(Frame &frame, ExecutionContext &context) override { SCOPED_PROFILE_OP("LoadCsv"); @@ -4674,12 +4672,14 @@ class LoadCsvCursor : public Cursor { bool input_pulled = input_cursor_->Pull(frame, context); - // If the input is Once, we have to keep going until we read all the rows, - // regardless of whether the pull on Once returned false. - // If we have e.g. MATCH(n) LOAD CSV ... AS x SET n.name = x.name, then we - // have to read at most cardinality(n) rows (but we can read less and stop - // pulling MATCH). - if (!input_is_once_ && !input_pulled) return false; + if (input_pulled) { + if (did_pull_) { + throw QueryRuntimeException( + "LOAD CSV can be executed only once, please check if the cardinality of the operator before LOAD CSV is 1"); + } + did_pull_ = true; + } + auto row = reader_->GetNextRow(context.evaluation_context.memory); if (!row) { return false; diff --git a/src/query/plan/pretty_print.cpp b/src/query/plan/pretty_print.cpp index 1d0512d85..3b5c2303b 100644 --- a/src/query/plan/pretty_print.cpp +++ b/src/query/plan/pretty_print.cpp @@ -874,11 +874,27 @@ bool PlanToJsonVisitor::PreVisit(query::plan::CallProcedure &op) { bool PlanToJsonVisitor::PreVisit(query::plan::LoadCsv &op) { json self; self["name"] = "LoadCsv"; - self["file"] = ToJson(op.file_); - self["with_header"] = op.with_header_; - self["ignore_bad"] = op.ignore_bad_; - self["delimiter"] = ToJson(op.delimiter_); - self["quote"] = ToJson(op.quote_); + + if (op.file_) { + self["file"] = ToJson(op.file_); + } + + if (op.with_header_) { + self["with_header"] = op.with_header_; + } + + if (op.ignore_bad_) { + self["ignore_bad"] = op.ignore_bad_; + } + + if (op.delimiter_) { + self["delimiter"] = ToJson(op.delimiter_); + } + + if (op.quote_) { + self["quote"] = ToJson(op.quote_); + } + self["row_variable"] = ToJson(op.row_var_); op.input_->Accept(*this); diff --git a/src/query/plan/rewrite/index_lookup.hpp b/src/query/plan/rewrite/index_lookup.hpp index 1bcf2cb09..feac431fe 100644 --- a/src/query/plan/rewrite/index_lookup.hpp +++ b/src/query/plan/rewrite/index_lookup.hpp @@ -477,6 +477,16 @@ class IndexLookupRewriter final : public HierarchicalLogicalOperatorVisitor { return true; } + bool PreVisit(LoadCsv &op) override { + prev_ops_.push_back(&op); + return true; + } + + bool PostVisit(LoadCsv & /*op*/) override { + prev_ops_.pop_back(); + return true; + } + std::shared_ptr new_root_; private: diff --git a/src/utils/csv_parsing.cpp b/src/utils/csv_parsing.cpp index 49d8a0949..4744f2100 100644 --- a/src/utils/csv_parsing.cpp +++ b/src/utils/csv_parsing.cpp @@ -1,4 +1,4 @@ -// Copyright 2022 Memgraph Ltd. +// Copyright 2023 Memgraph Ltd. // // Use of this software is governed by the Business Source License // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source diff --git a/src/utils/csv_parsing.hpp b/src/utils/csv_parsing.hpp index 37d438b41..928654ca8 100644 --- a/src/utils/csv_parsing.hpp +++ b/src/utils/csv_parsing.hpp @@ -1,4 +1,4 @@ -// Copyright 2022 Memgraph Ltd. +// Copyright 2023 Memgraph Ltd. // // Use of this software is governed by the Business Source License // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source diff --git a/tests/unit/utils_csv_parsing.cpp b/tests/unit/utils_csv_parsing.cpp index 3c852b171..9fef48af1 100644 --- a/tests/unit/utils_csv_parsing.cpp +++ b/tests/unit/utils_csv_parsing.cpp @@ -1,4 +1,4 @@ -// Copyright 2022 Memgraph Ltd. +// Copyright 2023 Memgraph Ltd. // // Use of this software is governed by the Business Source License // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source