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
1 change: 1 addition & 0 deletions query/AST/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ set(ast_sources
stmt/CallSubqueryStmt.cpp
stmt/CreateStmt.cpp
stmt/MergeStmt.cpp
stmt/NextSegmentStmt.cpp
stmt/DeleteStmt.cpp
stmt/ReturnStmt.cpp
stmt/ShortestPathStmt.cpp
Expand Down
9 changes: 6 additions & 3 deletions query/AST/CypherAST.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class CallStmt;
class CallSubqueryStmt;
class CreateStmt;
class MergeStmt;
class NextSegmentStmt;
class SetStmt;
class RemoveStmt;
class DeleteStmt;
Expand Down Expand Up @@ -180,6 +181,7 @@ class CypherAST {
friend CallSubqueryStmt;
friend CreateStmt;
friend MergeStmt;
friend NextSegmentStmt;
friend SetStmt;
friend RemoveStmt;
friend DeleteStmt;
Expand Down Expand Up @@ -238,6 +240,10 @@ class CypherAST {

const QueryCommands& queries() const { return _queries; }

// Moves a command out of the top-level list and into the one a holder reads it
// through, once that holder is built over it
void nestQuery(QueryCommand* query);

const std::vector<Expr*>& getExpressions() const { return _expressions; }
const std::vector<EntityPattern*>& getEntityPatterns() const { return _entityPatterns; }

Expand Down Expand Up @@ -321,9 +327,6 @@ class CypherAST {
void addStmtContainer(StmtContainer* container);
void addQuery(QueryCommand* query);

// Moves a command out of the top-level list and into the one a holder reads it
// through, once that holder is built over it
void nestQuery(QueryCommand* query);
void addDeclContext(DeclContext* ctxt);
void addVarDecl(VarDecl* decl);
void addNodePatternData(NodePatternData* data);
Expand Down
14 changes: 14 additions & 0 deletions query/AST/CypherASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "stmt/ReturnStmt.h"
#include "stmt/CallStmt.h"
#include "stmt/CallSubqueryStmt.h"
#include "stmt/NextSegmentStmt.h"
#include "stmt/DeleteStmt.h"
#include "stmt/SetStmt.h"
#include "stmt/Limit.h"
Expand Down Expand Up @@ -357,6 +358,19 @@ void CypherASTDumper::dumpQueryBody(std::ostream& out, const SinglePartQuery* qu
}
break;

case Stmt::Kind::NEXT_SEGMENT: {
const NextSegmentStmt* segment = static_cast<const NextSegmentStmt*>(stmt);
out << " _" << std::hex << query << " ||--o{ _" << std::hex << segment << " : \"\"\n";
out << " _" << std::hex << segment << " {\n";
out << " ASTType NEXT_SEGMENT\n";
out << " }\n";
const CallSubqueryStmt* body = segment->getBody();
for (const CallSubqueryStmt::Branch& branch : body->branches()) {
dump(out, branch._query);
}
}
break;

case Stmt::Kind::UNWIND: {
const UnwindStmt* unwStmt = static_cast<const UnwindStmt*>(stmt);
const Expr* arg = unwStmt->arg();
Expand Down
1 change: 1 addition & 0 deletions query/AST/SinglePartQuery.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class SinglePartQuery : public QueryCommand {

const StmtContainer* getStmts() const { return _stmts; }
const ReturnStmt* getReturnStmt() const { return _returnStmt; }
ReturnStmt* getReturnStmt() { return _returnStmt; }

// A CALL subquery counts when its own body does
bool writesToTheGraph() const;
Expand Down
5 changes: 5 additions & 0 deletions query/AST/stmt/CallSubqueryStmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class CypherAST;
class Expr;
class SinglePartQuery;
class Symbol;
class VarDecl;

// CALL (a, b) { ... }: a query of its own, run once per row in flight, reading the
// variables it imports and nothing else of the scope around it
Expand All @@ -29,6 +30,10 @@ class CallSubqueryStmt final : public Stmt {
Imports _imports;
Expr* _condition {nullptr};
CallSubqueryStmt* _whenCall {nullptr};

// The declaration the analyzer binds each import to, which a later clause of the
// body may name again
std::vector<const VarDecl*> _importDecls;
};

using Branches = std::vector<Branch>;
Expand Down
19 changes: 19 additions & 0 deletions query/AST/stmt/NextSegmentStmt.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#include "NextSegmentStmt.h"

#include "CypherAST.h"

using namespace db;

NextSegmentStmt::NextSegmentStmt(CallSubqueryStmt* body)
: _body(body)
{
}

NextSegmentStmt::~NextSegmentStmt() {
}

NextSegmentStmt* NextSegmentStmt::create(CypherAST* ast, CallSubqueryStmt* body) {
NextSegmentStmt* stmt = new NextSegmentStmt(body);
ast->addStmt(stmt);
return stmt;
}
39 changes: 39 additions & 0 deletions query/AST/stmt/NextSegmentStmt.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#pragma once

#include <vector>

#include "Stmt.h"

namespace db {

class CallSubqueryStmt;
class CypherAST;
class VarDecl;

// A UNION or a WHEN that is one segment of a NEXT chain, or a segment ending on an
// updating clause that NEXT follows. It reads the rows the segments before it returned
// and its RETURN replaces them. A UNION reads the whole table in every branch; a WHEN
// decides per row, as the CALL (*) { WHEN ... } its body is written as would.
class NextSegmentStmt final : public Stmt {
public:
static NextSegmentStmt* create(CypherAST* ast, CallSubqueryStmt* body);

Kind getKind() const final { return Kind::NEXT_SEGMENT; }

CallSubqueryStmt* getBody() const { return _body; }

// What the scope clause of a CALL around a segment that returns nothing imports,
// declared again after it: the segment's table is gone, the CALL's row is not
const std::vector<const VarDecl*>& getCarriedImports() const { return _carriedImports; }

void addCarriedImport(const VarDecl* decl) { _carriedImports.push_back(decl); }

private:
CallSubqueryStmt* _body {nullptr};
std::vector<const VarDecl*> _carriedImports;

explicit NextSegmentStmt(CallSubqueryStmt* body);
~NextSegmentStmt() final;
};

}
37 changes: 22 additions & 15 deletions query/AST/stmt/Stmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "SinglePartQuery.h"
#include "CallSubqueryStmt.h"
#include "NextSegmentStmt.h"

using namespace db;

Expand All @@ -27,53 +28,59 @@ bool Stmt::isUpdating(Kind kind) {
case Kind::UNWIND:
case Kind::WITH:
case Kind::CALL_SUBQUERY:
case Kind::NEXT_SEGMENT:
return false;
break;
}

return false;
}

bool Stmt::isUpdating(const Stmt* stmt) {
const CallSubqueryStmt* Stmt::getSubqueryBody(const Stmt* stmt) {
const Kind kind = stmt->getKind();

if (kind == Kind::CALL_SUBQUERY) {
const CallSubqueryStmt* subquery = static_cast<const CallSubqueryStmt*>(stmt);

return !subquery->isReturning() && writesToTheGraph(stmt);
return static_cast<const CallSubqueryStmt*>(stmt);
} else if (kind == Kind::NEXT_SEGMENT) {
return static_cast<const NextSegmentStmt*>(stmt)->getBody();
}

return isUpdating(kind);
return nullptr;
}

bool Stmt::writesToTheGraph(const Stmt* stmt) {
const Kind kind = stmt->getKind();
bool Stmt::isUpdating(const Stmt* stmt) {
if (const CallSubqueryStmt* subquery = getSubqueryBody(stmt)) {
return !subquery->isReturning() && subquery->writesToTheGraph();
}

if (kind == Kind::CALL_SUBQUERY) {
const CallSubqueryStmt* subquery = static_cast<const CallSubqueryStmt*>(stmt);
return isUpdating(stmt->getKind());
}

bool Stmt::writesToTheGraph(const Stmt* stmt) {
if (const CallSubqueryStmt* subquery = getSubqueryBody(stmt)) {
return subquery->writesToTheGraph();
}

return isUpdating(kind);
return isUpdating(stmt->getKind());
}

bool Stmt::isReading(const Stmt* stmt) {
const Kind kind = stmt->getKind();

if (kind == Kind::CALL_SUBQUERY) {
if (getSubqueryBody(stmt)) {
return readsTheGraph(stmt);
}

return !isUpdating(kind);
return !isUpdating(stmt->getKind());
}

bool Stmt::readsTheGraph(const Stmt* stmt) {
const Kind kind = stmt->getKind();

switch (kind) {
case Kind::CALL_SUBQUERY:
return static_cast<const CallSubqueryStmt*>(stmt)->readsTheGraph();
case Kind::NEXT_SEGMENT: {
const CallSubqueryStmt* subquery = getSubqueryBody(stmt);
return subquery->readsTheGraph();
}
break;

case Kind::MATCH:
Expand Down
5 changes: 5 additions & 0 deletions query/AST/stmt/Stmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace db {

class CallSubqueryStmt;
class CypherAST;

class Stmt {
Expand All @@ -23,6 +24,7 @@ class Stmt {
UNWIND,
WITH,
CALL_SUBQUERY,
NEXT_SEGMENT,
};

// A clause that writes to the graph. Cypher orders a query part's clauses reading
Expand All @@ -48,6 +50,9 @@ class Stmt {
// nothing an updating clause above it could hide
static bool isReading(const Stmt* stmt);

// The query a CALL subquery or a NEXT segment runs, null for any other clause
static const CallSubqueryStmt* getSubqueryBody(const Stmt* stmt);

virtual Kind getKind() const = 0;

protected:
Expand Down
5 changes: 5 additions & 0 deletions query/AST/stmt/WithStmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,18 @@ class WithStmt final : public Stmt {
// The variables the WHERE reads that the projection drops
const std::vector<const VarDecl*>& filterImports() const { return _filterImports; }

// Whether this is the RETURN a NEXT follows, which imports nothing into a CALL body
bool isSegmentReturn() const { return _segmentReturn; }

void setWhere(WhereClause* where) { _where = where; }
void addFilterImport(const VarDecl* decl) { _filterImports.push_back(decl); }
void setSegmentReturn(bool segmentReturn) { _segmentReturn = segmentReturn; }

private:
Projection* _projection {nullptr};
WhereClause* _where {nullptr};
std::vector<const VarDecl*> _filterImports;
bool _segmentReturn {false};

explicit WithStmt(Projection* projection);
~WithStmt() final;
Expand Down
Loading
Loading