Add parsing and planning of basic user management queries
Reviewers: teon.banek, mferencevic Reviewed By: teon.banek, mferencevic Subscribers: pullbot, buda Differential Revision: https://phabricator.memgraph.io/D1398
This commit is contained in:
parent
3948cea83c
commit
b9be394cb2
@ -48,7 +48,7 @@ cpp<#
|
||||
(symbol-table "query::SymbolTable" :capnp-type "Sem.SymbolTable")
|
||||
(storage "query::AstStorage" :initarg nil
|
||||
:save-fun ""
|
||||
:load-fun "storage = std::move(ar.template get_helper<query::AstTreeStorage>(query::AstTreeStorage::kHelperId));"
|
||||
:load-fun "storage = std::move(ar.template get_helper<query::AstStorage>(query::AstStorage::kHelperId));"
|
||||
:capnp-save :dont-save)))
|
||||
(:response ()))
|
||||
|
||||
|
@ -115,4 +115,11 @@ class RemoveAttachedVertexException : public QueryRuntimeException {
|
||||
"connections. Consider using DETACH DELETE.") {}
|
||||
};
|
||||
|
||||
class UserModificationInMulticommandTxException : public QueryException {
|
||||
public:
|
||||
UserModificationInMulticommandTxException()
|
||||
: QueryException(
|
||||
"User modification not allowed in multicommand transactions") {}
|
||||
};
|
||||
|
||||
} // namespace query
|
||||
|
@ -131,6 +131,8 @@ struct Clause {
|
||||
merge @10 :Merge;
|
||||
unwind @11 :Unwind;
|
||||
createIndex @12 :CreateIndex;
|
||||
modifyUser @13 :ModifyUser;
|
||||
dropUser @14 :DropUser;
|
||||
}
|
||||
}
|
||||
|
||||
@ -383,3 +385,12 @@ struct CreateIndex {
|
||||
property @1 :Storage.Common;
|
||||
}
|
||||
|
||||
struct ModifyUser {
|
||||
username @0 :Text;
|
||||
password @1 :Tree;
|
||||
isCreate @2 :Bool;
|
||||
}
|
||||
|
||||
struct DropUser {
|
||||
usernames @0 :List(Text);
|
||||
}
|
||||
|
@ -1249,6 +1249,14 @@ Clause *Clause::Construct(const capnp::Clause::Reader &reader,
|
||||
auto with_reader = reader.getWith();
|
||||
return With::Construct(with_reader, storage);
|
||||
}
|
||||
case capnp::Clause::MODIFY_USER: {
|
||||
auto mu_reader = reader.getModifyUser();
|
||||
return ModifyUser::Construct(mu_reader, storage);
|
||||
}
|
||||
case capnp::Clause::DROP_USER: {
|
||||
auto du_reader = reader.getDropUser();
|
||||
return DropUser::Construct(du_reader, storage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1806,6 +1814,71 @@ With *With::Construct(const capnp::With::Reader &reader,
|
||||
return storage->Create<With>();
|
||||
}
|
||||
|
||||
// ModifyUser.
|
||||
void ModifyUser::Save(capnp::Clause::Builder *clause_builder,
|
||||
std::vector<int> *saved_uids) {
|
||||
Clause::Save(clause_builder, saved_uids);
|
||||
auto builder = clause_builder->initModifyUser();
|
||||
ModifyUser::Save(&builder, saved_uids);
|
||||
}
|
||||
|
||||
void ModifyUser::Save(capnp::ModifyUser::Builder *builder,
|
||||
std::vector<int> *saved_uids) {
|
||||
builder->setUsername(username_);
|
||||
if (password_) {
|
||||
auto password_builder = builder->getPassword();
|
||||
password_->Save(&password_builder, saved_uids);
|
||||
}
|
||||
builder->setIsCreate(is_create_);
|
||||
}
|
||||
|
||||
void ModifyUser::Load(const capnp::Tree::Reader &base_reader,
|
||||
AstStorage *storage, std::vector<int> *loaded_uids) {
|
||||
Clause::Load(base_reader, storage, loaded_uids);
|
||||
auto reader = base_reader.getClause().getModifyUser();
|
||||
username_ = reader.getUsername();
|
||||
if (reader.hasPassword()) {
|
||||
const auto password_reader = reader.getPassword();
|
||||
password_ =
|
||||
dynamic_cast<Expression *>(storage->Load(password_reader, loaded_uids));
|
||||
} else {
|
||||
password_ = nullptr;
|
||||
}
|
||||
is_create_ = reader.getIsCreate();
|
||||
}
|
||||
|
||||
ModifyUser *ModifyUser::Construct(const capnp::ModifyUser::Reader &reader,
|
||||
AstStorage *storage) {
|
||||
return storage->Create<ModifyUser>();
|
||||
}
|
||||
|
||||
// DropUser.
|
||||
void DropUser::Save(capnp::Clause::Builder *clause_builder,
|
||||
std::vector<int> *saved_uids) {
|
||||
Clause::Save(clause_builder, saved_uids);
|
||||
auto builder = clause_builder->initDropUser();
|
||||
DropUser::Save(&builder, saved_uids);
|
||||
}
|
||||
|
||||
void DropUser::Save(capnp::DropUser::Builder *builder,
|
||||
std::vector<int> *saved_uids) {
|
||||
auto usernames_builder = builder->initUsernames(usernames_.size());
|
||||
utils::SaveVector(usernames_, &usernames_builder);
|
||||
}
|
||||
|
||||
void DropUser::Load(const capnp::Tree::Reader &base_reader,
|
||||
AstStorage *storage, std::vector<int> *loaded_uids) {
|
||||
Clause::Load(base_reader, storage, loaded_uids);
|
||||
auto reader = base_reader.getClause().getDropUser();
|
||||
usernames_.clear();
|
||||
utils::LoadVector(&usernames_, reader.getUsernames());
|
||||
}
|
||||
|
||||
DropUser *DropUser::Construct(const capnp::DropUser::Reader &reader,
|
||||
AstStorage *storage) {
|
||||
return storage->Create<DropUser>();
|
||||
}
|
||||
|
||||
// CypherUnion
|
||||
void CypherUnion::Save(capnp::Tree::Builder *tree_builder,
|
||||
std::vector<int> *saved_uids) {
|
||||
@ -2367,3 +2440,5 @@ BOOST_CLASS_EXPORT_IMPLEMENT(query::Unwind);
|
||||
BOOST_CLASS_EXPORT_IMPLEMENT(query::Identifier);
|
||||
BOOST_CLASS_EXPORT_IMPLEMENT(query::PrimitiveLiteral);
|
||||
BOOST_CLASS_EXPORT_IMPLEMENT(query::CreateIndex);
|
||||
BOOST_CLASS_EXPORT_IMPLEMENT(query::ModifyUser);
|
||||
BOOST_CLASS_EXPORT_IMPLEMENT(query::DropUser);
|
||||
|
@ -3499,6 +3499,100 @@ class CreateIndex : public Clause {
|
||||
const unsigned int);
|
||||
};
|
||||
|
||||
class ModifyUser : public Clause {
|
||||
friend class AstStorage;
|
||||
|
||||
public:
|
||||
DEFVISITABLE(TreeVisitor<TypedValue>);
|
||||
DEFVISITABLE(HierarchicalTreeVisitor);
|
||||
|
||||
ModifyUser *Clone(AstStorage &storage) const override {
|
||||
return storage.Create<ModifyUser>(
|
||||
username_, password_ ? password_->Clone(storage) : nullptr, is_create_);
|
||||
}
|
||||
|
||||
static ModifyUser *Construct(const capnp::ModifyUser::Reader &reader,
|
||||
AstStorage *storage);
|
||||
using Clause::Save;
|
||||
|
||||
std::string username_;
|
||||
Expression *password_;
|
||||
bool is_create_;
|
||||
|
||||
protected:
|
||||
explicit ModifyUser(int uid) : Clause(uid) {}
|
||||
ModifyUser(int uid, std::string username, Expression *password,
|
||||
bool is_create)
|
||||
: Clause(uid),
|
||||
username_(std::move(username)),
|
||||
password_(password),
|
||||
is_create_(is_create) {}
|
||||
|
||||
void Save(capnp::Clause::Builder *builder,
|
||||
std::vector<int> *saved_uids) override;
|
||||
virtual void Save(capnp::ModifyUser::Builder *builder,
|
||||
std::vector<int> *saved_uids);
|
||||
void Load(const capnp::Tree::Reader &base_reader, AstStorage *storage,
|
||||
std::vector<int> *loaded_uids) override;
|
||||
|
||||
private:
|
||||
friend class boost::serialization::access;
|
||||
|
||||
template <class TArchive>
|
||||
void serialize(TArchive &ar, const unsigned int) {
|
||||
ar &boost::serialization::base_object<Clause>(*this);
|
||||
ar &username_ &password_ &is_create_;
|
||||
}
|
||||
|
||||
template <class TArchive>
|
||||
friend void boost::serialization::load_construct_data(TArchive &,
|
||||
ModifyUser *,
|
||||
const unsigned int);
|
||||
};
|
||||
|
||||
class DropUser : public Clause {
|
||||
friend class AstStorage;
|
||||
|
||||
public:
|
||||
DEFVISITABLE(TreeVisitor<TypedValue>);
|
||||
DEFVISITABLE(HierarchicalTreeVisitor);
|
||||
|
||||
DropUser *Clone(AstStorage &storage) const override {
|
||||
return storage.Create<DropUser>(usernames_);
|
||||
}
|
||||
|
||||
static DropUser *Construct(const capnp::DropUser::Reader &reader,
|
||||
AstStorage *storage);
|
||||
using Clause::Save;
|
||||
|
||||
std::vector<std::string> usernames_;
|
||||
|
||||
protected:
|
||||
explicit DropUser(int uid) : Clause(uid) {}
|
||||
DropUser(int uid, std::vector<std::string> usernames)
|
||||
: Clause(uid), usernames_(usernames) {}
|
||||
|
||||
void Save(capnp::Clause::Builder *builder,
|
||||
std::vector<int> *saved_uids) override;
|
||||
virtual void Save(capnp::DropUser::Builder *builder,
|
||||
std::vector<int> *saved_uids);
|
||||
void Load(const capnp::Tree::Reader &base_reader, AstStorage *storage,
|
||||
std::vector<int> *loaded_uids) override;
|
||||
|
||||
private:
|
||||
friend class boost::serialization::access;
|
||||
|
||||
template <class TArchive>
|
||||
void serialize(TArchive &ar, const unsigned int) {
|
||||
ar &boost::serialization::base_object<Clause>(*this);
|
||||
ar &usernames_;
|
||||
}
|
||||
|
||||
template <class TArchive>
|
||||
friend void boost::serialization::load_construct_data(TArchive &, DropUser *,
|
||||
const unsigned int);
|
||||
};
|
||||
|
||||
#undef CLONE_BINARY_EXPRESSION
|
||||
#undef CLONE_UNARY_EXPRESSION
|
||||
#undef SERIALIZE_USING_BASE
|
||||
@ -3573,6 +3667,8 @@ LOAD_AND_CONSTRUCT(query::RemoveLabels, 0);
|
||||
LOAD_AND_CONSTRUCT(query::Merge, 0);
|
||||
LOAD_AND_CONSTRUCT(query::Unwind, 0);
|
||||
LOAD_AND_CONSTRUCT(query::CreateIndex, 0);
|
||||
LOAD_AND_CONSTRUCT(query::ModifyUser, 0);
|
||||
LOAD_AND_CONSTRUCT(query::DropUser, 0);
|
||||
|
||||
} // namespace boost::serialization
|
||||
|
||||
@ -3633,3 +3729,5 @@ BOOST_CLASS_EXPORT_KEY(query::Unwind);
|
||||
BOOST_CLASS_EXPORT_KEY(query::Identifier);
|
||||
BOOST_CLASS_EXPORT_KEY(query::PrimitiveLiteral);
|
||||
BOOST_CLASS_EXPORT_KEY(query::CreateIndex);
|
||||
BOOST_CLASS_EXPORT_KEY(query::ModifyUser);
|
||||
BOOST_CLASS_EXPORT_KEY(query::DropUser);
|
||||
|
@ -60,6 +60,8 @@ class RemoveLabels;
|
||||
class Merge;
|
||||
class Unwind;
|
||||
class CreateIndex;
|
||||
class ModifyUser;
|
||||
class DropUser;
|
||||
|
||||
using TreeCompositeVisitor = ::utils::CompositeVisitor<
|
||||
Query, SingleQuery, CypherUnion, NamedExpression, OrOperator, XorOperator,
|
||||
@ -73,8 +75,9 @@ using TreeCompositeVisitor = ::utils::CompositeVisitor<
|
||||
Pattern, NodeAtom, EdgeAtom, Delete, Where, SetProperty, SetProperties,
|
||||
SetLabels, RemoveProperty, RemoveLabels, Merge, Unwind>;
|
||||
|
||||
using TreeLeafVisitor = ::utils::LeafVisitor<Identifier, PrimitiveLiteral,
|
||||
ParameterLookup, CreateIndex>;
|
||||
using TreeLeafVisitor =
|
||||
::utils::LeafVisitor<Identifier, PrimitiveLiteral, ParameterLookup,
|
||||
CreateIndex, ModifyUser, DropUser>;
|
||||
|
||||
class HierarchicalTreeVisitor : public TreeCompositeVisitor,
|
||||
public TreeLeafVisitor {
|
||||
@ -97,6 +100,6 @@ using TreeVisitor = ::utils::Visitor<
|
||||
LabelsTest, Aggregation, Function, Reduce, All, Single, ParameterLookup,
|
||||
Create, Match, Return, With, Pattern, NodeAtom, EdgeAtom, Delete, Where,
|
||||
SetProperty, SetProperties, SetLabels, RemoveProperty, RemoveLabels, Merge,
|
||||
Unwind, Identifier, PrimitiveLiteral, CreateIndex>;
|
||||
Unwind, Identifier, PrimitiveLiteral, CreateIndex, ModifyUser, DropUser>;
|
||||
|
||||
} // namespace query
|
||||
|
@ -81,6 +81,7 @@ antlrcpp::Any CypherMainVisitor::visitSingleQuery(
|
||||
bool has_return = false;
|
||||
bool has_optional_match = false;
|
||||
bool has_create_index = false;
|
||||
bool has_modify_user = false;
|
||||
for (Clause *clause : single_query->clauses_) {
|
||||
if (dynamic_cast<Unwind *>(clause)) {
|
||||
if (has_update || has_return) {
|
||||
@ -125,11 +126,21 @@ antlrcpp::Any CypherMainVisitor::visitSingleQuery(
|
||||
"CreateIndex must be only clause in the query.");
|
||||
}
|
||||
has_create_index = true;
|
||||
} else if (dynamic_cast<ModifyUser *>(clause)) {
|
||||
has_modify_user = true;
|
||||
if (single_query->clauses_.size() != 1U) {
|
||||
throw SemanticException("ModifyUser must be only clause in the query.");
|
||||
}
|
||||
} else if (dynamic_cast<DropUser *>(clause)) {
|
||||
has_modify_user = true;
|
||||
if (single_query->clauses_.size() != 1U) {
|
||||
throw SemanticException("DropUser must be only clause in the query.");
|
||||
}
|
||||
} else {
|
||||
DLOG(FATAL) << "Can't happen";
|
||||
}
|
||||
}
|
||||
if (!has_update && !has_return && !has_create_index) {
|
||||
if (!has_update && !has_return && !has_create_index && !has_modify_user) {
|
||||
throw SemanticException(
|
||||
"Query should either update something, return results or create an "
|
||||
"index");
|
||||
@ -186,6 +197,14 @@ antlrcpp::Any CypherMainVisitor::visitClause(CypherParser::ClauseContext *ctx) {
|
||||
return static_cast<Clause *>(
|
||||
ctx->createIndex()->accept(this).as<CreateIndex *>());
|
||||
}
|
||||
if (ctx->modifyUser()) {
|
||||
return static_cast<Clause *>(
|
||||
ctx->modifyUser()->accept(this).as<ModifyUser *>());
|
||||
}
|
||||
if (ctx->dropUser()) {
|
||||
return static_cast<Clause *>(
|
||||
ctx->dropUser()->accept(this).as<DropUser *>());
|
||||
}
|
||||
// TODO: implement other clauses.
|
||||
throw utils::NotYetImplemented("clause '{}'", ctx->getText());
|
||||
return 0;
|
||||
@ -219,6 +238,49 @@ antlrcpp::Any CypherMainVisitor::visitCreateIndex(
|
||||
ctx_.db_accessor_.Label(ctx->labelName()->accept(this)), key.second);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ModifyUser*
|
||||
*/
|
||||
antlrcpp::Any CypherMainVisitor::visitModifyUser(
|
||||
CypherParser::ModifyUserContext *ctx) {
|
||||
std::string username(ctx->userName()->getText());
|
||||
Expression *password = nullptr;
|
||||
bool is_create = static_cast<bool>(ctx->createUser());
|
||||
for (auto option : ctx->modifyUserOption()) {
|
||||
if (option->passwordOption()) {
|
||||
if (password) {
|
||||
throw QueryException("password should be set at most once");
|
||||
}
|
||||
password = option->passwordOption()->accept(this);
|
||||
continue;
|
||||
}
|
||||
LOG(FATAL) << "Expected to handle all cases above.";
|
||||
}
|
||||
return storage_.Create<ModifyUser>(username, password, is_create);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Expression*
|
||||
*/
|
||||
antlrcpp::Any CypherMainVisitor::visitPasswordOption(
|
||||
CypherParser::PasswordOptionContext *ctx) {
|
||||
if (!ctx->literal()->StringLiteral() && !ctx->literal()->CYPHERNULL()) {
|
||||
throw SyntaxException("password should be a string literal or NULL");
|
||||
}
|
||||
return ctx->literal()->accept(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DropUser*
|
||||
*/
|
||||
antlrcpp::Any CypherMainVisitor::visitDropUser(
|
||||
CypherParser::DropUserContext *ctx) {
|
||||
std::vector<std::string> usernames;
|
||||
for (auto username_ptr : ctx->userName())
|
||||
usernames.emplace_back(username_ptr->getText());
|
||||
return storage_.Create<DropUser>(usernames);
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitCypherReturn(
|
||||
CypherParser::CypherReturnContext *ctx) {
|
||||
auto *return_clause = storage_.Create<Return>();
|
||||
@ -949,10 +1011,11 @@ antlrcpp::Any CypherMainVisitor::visitLiteral(
|
||||
return static_cast<Expression *>(
|
||||
storage_.Create<PrimitiveLiteral>(TypedValue::Null, token_position));
|
||||
} else if (ctx_.is_query_cached_) {
|
||||
// Instead of generating PrimitiveLiteral, we generate a ParameterLookup,
|
||||
// so that the AST can be cached. This allows for varying literals, which
|
||||
// are then looked up in the parameters table (even though they are not
|
||||
// user provided). Note, that NULL always generates a PrimitiveLiteral.
|
||||
// Instead of generating PrimitiveLiteral, we generate a
|
||||
// ParameterLookup, so that the AST can be cached. This allows for
|
||||
// varying literals, which are then looked up in the parameters table
|
||||
// (even though they are not user provided). Note, that NULL always
|
||||
// generates a PrimitiveLiteral.
|
||||
return static_cast<Expression *>(
|
||||
storage_.Create<ParameterLookup>(token_position));
|
||||
} else if (ctx->StringLiteral()) {
|
||||
|
@ -173,6 +173,19 @@ class CypherMainVisitor : public antlropencypher::CypherBaseVisitor {
|
||||
antlrcpp::Any visitCreateIndex(
|
||||
CypherParser::CreateIndexContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return ModifyUser*
|
||||
*/
|
||||
antlrcpp::Any visitModifyUser(CypherParser::ModifyUserContext *ctx) override;
|
||||
|
||||
antlrcpp::Any visitPasswordOption(
|
||||
CypherParser::PasswordOptionContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return DropUser*
|
||||
*/
|
||||
antlrcpp::Any visitDropUser(CypherParser::DropUserContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return Return*
|
||||
*/
|
||||
|
@ -47,6 +47,8 @@ clause : cypherMatch
|
||||
| with
|
||||
| cypherReturn
|
||||
| createIndex
|
||||
| modifyUser
|
||||
| dropUser
|
||||
;
|
||||
|
||||
cypherMatch : ( OPTIONAL SP )? MATCH SP? pattern ( SP? where )? ;
|
||||
@ -274,6 +276,20 @@ integerLiteral : HexInteger
|
||||
|
||||
createIndex : CREATE SP INDEX SP ON SP? ':' SP? labelName SP? '(' SP? propertyKeyName SP? ')' ;
|
||||
|
||||
userName : UnescapedSymbolicName ;
|
||||
|
||||
createUser : CREATE SP USER ;
|
||||
|
||||
alterUser : ALTER SP USER ;
|
||||
|
||||
modifyUser : ( createUser | alterUser ) SP userName ( SP WITH ( SP modifyUserOption )+ )? ;
|
||||
|
||||
modifyUserOption : passwordOption ;
|
||||
|
||||
passwordOption : PASSWORD SP literal;
|
||||
|
||||
dropUser : DROP SP USER SP userName ( SP? ',' SP? userName )* ;
|
||||
|
||||
HexInteger : '0x' ( HexDigit )+ ;
|
||||
|
||||
DecimalInteger : ZeroDigit
|
||||
@ -484,6 +500,14 @@ BFS : ( 'B' | 'b' ) ( 'F' | 'f' ) ( 'S' | 's' ) ;
|
||||
|
||||
WSHORTEST : ( 'W' | 'w' ) ( 'S' | 's' ) ( 'H' | 'h' ) ( 'O' | 'o' ) ( 'R' | 'r' ) ( 'T' | 't' ) ( 'E' | 'e' ) ( 'S' | 's' ) ( 'T' | 't' ) ;
|
||||
|
||||
USER : ( 'U' | 'u' ) ( 'S' | 's' ) ( 'E' | 'e' ) ( 'R' | 'r' ) ;
|
||||
|
||||
PASSWORD : ( 'P' | 'p' ) ( 'A' | 'a' ) ( 'S' | 's' ) ( 'S' | 's' ) ( 'W' | 'w' ) ( 'O' | 'o' ) ( 'R' | 'r' ) ( 'D' | 'd' ) ;
|
||||
|
||||
ALTER : ( 'A' | 'a' ) ( 'L' | 'l' ) ( 'T' | 't' ) ( 'E' | 'e' ) ( 'R' | 'r' ) ;
|
||||
|
||||
DROP : ( 'D' | 'd' ) ( 'R' | 'r' ) ( 'O' | 'o' ) ( 'P' | 'p' ) ;
|
||||
|
||||
UnescapedSymbolicName : IdentifierStart ( IdentifierPart )* ;
|
||||
|
||||
/**
|
||||
@ -620,4 +644,3 @@ fragment VT : [\u000B] ;
|
||||
fragment US : [\u001F] ;
|
||||
|
||||
fragment ID_Start : [A-Za-z\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376-\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E-\u066F\u0671-\u06D3\u06D5\u06E5-\u06E6\u06EE-\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4-\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F-\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC-\u09DD\u09DF-\u09E1\u09F0-\u09F1\u0A05-\u0A0A\u0A0F-\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32-\u0A33\u0A35-\u0A36\u0A38-\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2-\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0-\u0AE1\u0B05-\u0B0C\u0B0F-\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32-\u0B33\u0B35-\u0B39\u0B3D\u0B5C-\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99-\u0B9A\u0B9C\u0B9E-\u0B9F\u0BA3-\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58-\u0C59\u0C60-\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0-\u0CE1\u0CF1-\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32-\u0E33\u0E40-\u0E46\u0E81-\u0E82\u0E84\u0E87-\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA-\u0EAB\u0EAD-\u0EB0\u0EB2-\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065-\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F0\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE-\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5-\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309B-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A-\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5-\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40-\uFB41\uFB43-\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC] ;
|
||||
|
||||
|
@ -220,6 +220,10 @@ bool SymbolGenerator::PostVisit(Match &) {
|
||||
|
||||
bool SymbolGenerator::Visit(CreateIndex &) { return true; }
|
||||
|
||||
bool SymbolGenerator::Visit(ModifyUser &) { return true; }
|
||||
|
||||
bool SymbolGenerator::Visit(DropUser &) { return true; }
|
||||
|
||||
// Expressions
|
||||
|
||||
SymbolGenerator::ReturnType SymbolGenerator::Visit(Identifier &ident) {
|
||||
|
@ -47,6 +47,8 @@ class SymbolGenerator : public HierarchicalTreeVisitor {
|
||||
bool PreVisit(Match &) override;
|
||||
bool PostVisit(Match &) override;
|
||||
bool Visit(CreateIndex &) override;
|
||||
bool Visit(ModifyUser &) override;
|
||||
bool Visit(DropUser &) override;
|
||||
|
||||
// Expressions
|
||||
ReturnType Visit(Identifier &) override;
|
||||
|
@ -56,6 +56,8 @@ class ExpressionEvaluator : public TreeVisitor<TypedValue> {
|
||||
BLOCK_VISIT(Merge);
|
||||
BLOCK_VISIT(Unwind);
|
||||
BLOCK_VISIT(CreateIndex);
|
||||
BLOCK_VISIT(ModifyUser);
|
||||
BLOCK_VISIT(DropUser);
|
||||
|
||||
#undef BLOCK_VISIT
|
||||
|
||||
|
@ -62,8 +62,8 @@ class CostEstimator : public HierarchicalLogicalOperatorVisitor {
|
||||
static constexpr double kUnwindNoLiteral{10.0};
|
||||
};
|
||||
|
||||
using HierarchicalLogicalOperatorVisitor::PreVisit;
|
||||
using HierarchicalLogicalOperatorVisitor::PostVisit;
|
||||
using HierarchicalLogicalOperatorVisitor::PreVisit;
|
||||
|
||||
CostEstimator(const TDbAccessor &db_accessor, const Parameters ¶meters)
|
||||
: db_accessor_(db_accessor), parameters(parameters) {}
|
||||
@ -185,6 +185,8 @@ class CostEstimator : public HierarchicalLogicalOperatorVisitor {
|
||||
|
||||
bool Visit(Once &) override { return true; }
|
||||
bool Visit(CreateIndex &) override { return true; }
|
||||
bool Visit(ModifyUser &) override { return true; }
|
||||
bool Visit(DropUser &) override { return true; }
|
||||
|
||||
// TODO: Cost estimate PullRemote and ProduceRemote?
|
||||
|
||||
|
@ -55,6 +55,8 @@ class IndependentSubtreeFinder : public HierarchicalLogicalOperatorVisitor {
|
||||
// These don't use any symbols
|
||||
bool Visit(Once &) override { return true; }
|
||||
bool Visit(CreateIndex &) override { return true; }
|
||||
bool Visit(ModifyUser &) override { return true; }
|
||||
bool Visit(DropUser &) override { return true; }
|
||||
|
||||
bool PostVisit(ScanAll &scan) override { return true; }
|
||||
bool PostVisit(ScanAllByLabel &scan) override { return true; }
|
||||
@ -683,6 +685,10 @@ class DistributedPlanner : public HierarchicalLogicalOperatorVisitor {
|
||||
|
||||
bool Visit(CreateIndex &) override { return true; }
|
||||
|
||||
bool Visit(ModifyUser &) override { return true; }
|
||||
|
||||
bool Visit(DropUser &) override { return true; }
|
||||
|
||||
// Accumulate is used only if the query performs any writes. In such a case,
|
||||
// we need to synchronize the work done on master and all workers.
|
||||
// Synchronization will force applying changes to distributed storage, and
|
||||
|
@ -423,8 +423,8 @@ std::unique_ptr<Cursor> ScanAllByLabelPropertyRange::MakeCursor(
|
||||
return std::experimental::make_optional(
|
||||
utils::Bound<PropertyValue>(value, bound->type()));
|
||||
} catch (const TypedValueException &) {
|
||||
throw QueryRuntimeException(
|
||||
"'{}' cannot be used as a property value.", value.type());
|
||||
throw QueryRuntimeException("'{}' cannot be used as a property value.",
|
||||
value.type());
|
||||
}
|
||||
};
|
||||
auto maybe_lower = convert(lower_bound());
|
||||
@ -466,11 +466,11 @@ std::unique_ptr<Cursor> ScanAllByLabelPropertyValue::MakeCursor(
|
||||
auto value = expression_->Accept(evaluator);
|
||||
if (value.IsNull()) return std::experimental::nullopt;
|
||||
try {
|
||||
return std::experimental::make_optional(db.Vertices(
|
||||
label_, property_, value, graph_view_ == GraphView::NEW));
|
||||
return std::experimental::make_optional(
|
||||
db.Vertices(label_, property_, value, graph_view_ == GraphView::NEW));
|
||||
} catch (const TypedValueException &) {
|
||||
throw QueryRuntimeException(
|
||||
"'{}' cannot be used as a property value.", value.type());
|
||||
throw QueryRuntimeException("'{}' cannot be used as a property value.",
|
||||
value.type());
|
||||
}
|
||||
};
|
||||
return std::make_unique<ScanAllCursor<decltype(vertices)>>(
|
||||
@ -1368,7 +1368,8 @@ class ExpandWeightedShortestPathCursor : public query::plan::Cursor {
|
||||
// For the given (edge, vertex, weight, depth) tuple checks if they
|
||||
// satisfy the "where" condition. if so, places them in the priority queue.
|
||||
auto expand_pair = [this, &evaluator, &frame, &create_state](
|
||||
EdgeAccessor edge, VertexAccessor vertex, double weight, int depth) {
|
||||
EdgeAccessor edge, VertexAccessor vertex,
|
||||
double weight, int depth) {
|
||||
SwitchAccessor(edge, self_.graph_view_);
|
||||
SwitchAccessor(vertex, self_.graph_view_);
|
||||
|
||||
@ -3827,6 +3828,74 @@ std::unique_ptr<Cursor> PullRemoteOrderBy::MakeCursor(
|
||||
return std::make_unique<PullRemoteOrderByCursor>(*this, db);
|
||||
}
|
||||
|
||||
ModifyUser::ModifyUser(std::string username, Expression *password,
|
||||
bool is_create)
|
||||
: username_(std::move(username)),
|
||||
password_(password),
|
||||
is_create_(is_create) {}
|
||||
|
||||
bool ModifyUser::Accept(HierarchicalLogicalOperatorVisitor &visitor) {
|
||||
return visitor.Visit(*this);
|
||||
}
|
||||
|
||||
WITHOUT_SINGLE_INPUT(ModifyUser)
|
||||
|
||||
class ModifyUserCursor : public Cursor {
|
||||
public:
|
||||
ModifyUserCursor(const ModifyUser &self, database::GraphDbAccessor &db)
|
||||
: self_(self), db_(db) {}
|
||||
|
||||
bool Pull(Frame &frame, Context &ctx) override {
|
||||
if (ctx.in_explicit_transaction_) {
|
||||
throw UserModificationInMulticommandTxException();
|
||||
}
|
||||
ExpressionEvaluator evaluator(frame, ctx.parameters_, ctx.symbol_table_,
|
||||
db_, GraphView::OLD);
|
||||
throw utils::NotYetImplemented("user auth");
|
||||
}
|
||||
|
||||
void Reset() override { throw utils::NotYetImplemented("user auth"); }
|
||||
|
||||
private:
|
||||
const ModifyUser &self_;
|
||||
database::GraphDbAccessor &db_;
|
||||
};
|
||||
|
||||
std::unique_ptr<Cursor> ModifyUser::MakeCursor(
|
||||
database::GraphDbAccessor &db) const {
|
||||
return std::make_unique<ModifyUserCursor>(*this, db);
|
||||
}
|
||||
|
||||
bool DropUser::Accept(HierarchicalLogicalOperatorVisitor &visitor) {
|
||||
return visitor.Visit(*this);
|
||||
}
|
||||
|
||||
WITHOUT_SINGLE_INPUT(DropUser)
|
||||
|
||||
class DropUserCursor : public Cursor {
|
||||
public:
|
||||
DropUserCursor(const DropUser &self, database::GraphDbAccessor &db)
|
||||
: self_(self), db_(db) {}
|
||||
|
||||
bool Pull(Frame &, Context &ctx) override {
|
||||
if (ctx.in_explicit_transaction_) {
|
||||
throw UserModificationInMulticommandTxException();
|
||||
}
|
||||
throw utils::NotYetImplemented("user auth");
|
||||
}
|
||||
|
||||
void Reset() override { throw utils::NotYetImplemented("user auth"); }
|
||||
|
||||
private:
|
||||
const DropUser &self_;
|
||||
database::GraphDbAccessor &db_;
|
||||
};
|
||||
|
||||
std::unique_ptr<Cursor> DropUser::MakeCursor(
|
||||
database::GraphDbAccessor &db) const {
|
||||
return std::make_unique<DropUserCursor>(*this, db);
|
||||
}
|
||||
|
||||
} // namespace query::plan
|
||||
|
||||
BOOST_CLASS_EXPORT_IMPLEMENT(query::plan::Once);
|
||||
@ -3865,3 +3934,5 @@ BOOST_CLASS_EXPORT_IMPLEMENT(query::plan::PullRemote);
|
||||
BOOST_CLASS_EXPORT_IMPLEMENT(query::plan::Synchronize);
|
||||
BOOST_CLASS_EXPORT_IMPLEMENT(query::plan::Cartesian);
|
||||
BOOST_CLASS_EXPORT_IMPLEMENT(query::plan::PullRemoteOrderBy);
|
||||
BOOST_CLASS_EXPORT_IMPLEMENT(query::plan::ModifyUser);
|
||||
BOOST_CLASS_EXPORT_IMPLEMENT(query::plan::DropUser);
|
||||
|
@ -109,6 +109,8 @@ class PullRemote;
|
||||
class Synchronize;
|
||||
class Cartesian;
|
||||
class PullRemoteOrderBy;
|
||||
class ModifyUser;
|
||||
class DropUser;
|
||||
|
||||
using LogicalOperatorCompositeVisitor = ::utils::CompositeVisitor<
|
||||
Once, CreateNode, CreateExpand, ScanAll, ScanAllByLabel,
|
||||
@ -120,7 +122,8 @@ using LogicalOperatorCompositeVisitor = ::utils::CompositeVisitor<
|
||||
OrderBy, Merge, Optional, Unwind, Distinct, Union, PullRemote, Synchronize,
|
||||
Cartesian, PullRemoteOrderBy>;
|
||||
|
||||
using LogicalOperatorLeafVisitor = ::utils::LeafVisitor<Once, CreateIndex>;
|
||||
using LogicalOperatorLeafVisitor =
|
||||
::utils::LeafVisitor<Once, CreateIndex, ModifyUser, DropUser>;
|
||||
|
||||
/**
|
||||
* @brief Base class for hierarhical visitors of @c LogicalOperator class
|
||||
@ -2435,6 +2438,84 @@ by having only one result from each worker.")
|
||||
(:private #>cpp PullRemoteOrderBy() {} cpp<#)
|
||||
(:serialize :boost :capnp))
|
||||
|
||||
(lcp:define-class modify-user (logical-operator)
|
||||
((input "std::shared_ptr<LogicalOperator>"
|
||||
:capnp-save #'save-operator-pointer
|
||||
:capnp-load #'load-operator-pointer)
|
||||
(username "std::string" :reader t)
|
||||
(password "Expression *"
|
||||
:reader t
|
||||
:save-fun #'save-pointer
|
||||
:load-fun #'load-pointer
|
||||
:capnp-type "Ast.Tree"
|
||||
:capnp-init nil
|
||||
:capnp-save #'save-ast-pointer
|
||||
:capnp-load (load-ast-pointer "Expression *"))
|
||||
(is-create :bool :reader t))
|
||||
(:documentation
|
||||
"Operator that creates a new database user or modifies an existing one.")
|
||||
(:public
|
||||
#>cpp
|
||||
ModifyUser(std::string username, Expression *password, bool is_create);
|
||||
|
||||
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
||||
std::unique_ptr<Cursor> MakeCursor(
|
||||
database::GraphDbAccessor &db) const override;
|
||||
|
||||
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override {
|
||||
return std::vector<Symbol>();
|
||||
}
|
||||
|
||||
bool HasSingleInput() const override;
|
||||
std::shared_ptr<LogicalOperator> input() const override;
|
||||
void set_input(std::shared_ptr<LogicalOperator> input) override;
|
||||
cpp<#)
|
||||
(:private
|
||||
#>cpp
|
||||
friend class boost::serialization::access;
|
||||
|
||||
ModifyUser() {}
|
||||
cpp<#)
|
||||
(:serialize :boost :capnp))
|
||||
|
||||
(lcp:define-class drop-user (logical-operator)
|
||||
((input "std::shared_ptr<LogicalOperator>"
|
||||
:capnp-save #'save-operator-pointer
|
||||
:capnp-load #'load-operator-pointer)
|
||||
(usernames "std::vector<std::string>" :reader t
|
||||
:capnp-save (lambda (builder member-name)
|
||||
#>cpp
|
||||
utils::SaveVector(${member-name}, &${builder});
|
||||
cpp<#)
|
||||
:capnp-load (lambda (reader member-name)
|
||||
#>cpp
|
||||
utils::LoadVector(&${member-name}, ${reader});
|
||||
cpp<#)))
|
||||
(:documentation
|
||||
"Operator that deletes one or more existing database users.")
|
||||
(:public
|
||||
#>cpp
|
||||
DropUser(std::vector<std::string> usernames): usernames_(usernames) {}
|
||||
|
||||
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
||||
std::unique_ptr<Cursor> MakeCursor(
|
||||
database::GraphDbAccessor &db) const override;
|
||||
|
||||
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override {
|
||||
return std::vector<Symbol>();
|
||||
}
|
||||
|
||||
bool HasSingleInput() const override;
|
||||
std::shared_ptr<LogicalOperator> input() const override;
|
||||
void set_input(std::shared_ptr<LogicalOperator> input) override;
|
||||
cpp<#)
|
||||
(:private
|
||||
#>cpp
|
||||
friend class boost::serialization::access;
|
||||
DropUser() {}
|
||||
cpp<#)
|
||||
(:serialize :boost :capnp))
|
||||
|
||||
(lcp:pop-namespace) ;; plan
|
||||
(lcp:pop-namespace) ;; query
|
||||
|
||||
@ -2474,4 +2555,6 @@ BOOST_CLASS_EXPORT_KEY(query::plan::PullRemote);
|
||||
BOOST_CLASS_EXPORT_KEY(query::plan::Synchronize);
|
||||
BOOST_CLASS_EXPORT_KEY(query::plan::Cartesian);
|
||||
BOOST_CLASS_EXPORT_KEY(query::plan::PullRemoteOrderBy);
|
||||
BOOST_CLASS_EXPORT_KEY(query::plan::ModifyUser);
|
||||
BOOST_CLASS_EXPORT_KEY(query::plan::DropUser);
|
||||
cpp<#
|
||||
|
@ -53,6 +53,8 @@ class UsedSymbolsCollector : public HierarchicalTreeVisitor {
|
||||
bool Visit(PrimitiveLiteral &) override { return true; }
|
||||
bool Visit(ParameterLookup &) override { return true; }
|
||||
bool Visit(query::CreateIndex &) override { return true; }
|
||||
bool Visit(query::ModifyUser &) override { return true; }
|
||||
bool Visit(query::DropUser &) override { return true; }
|
||||
|
||||
std::unordered_set<Symbol> symbols_;
|
||||
const SymbolTable &symbol_table_;
|
||||
|
@ -387,6 +387,16 @@ class ReturnBodyContext : public HierarchicalTreeVisitor {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Visit(query::ModifyUser &) override {
|
||||
has_aggregation_.emplace_back(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Visit(query::DropUser &) override {
|
||||
has_aggregation_.emplace_back(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Creates NamedExpression with an Identifier for each user declared symbol.
|
||||
// This should be used when body.all_identifiers is true, to generate
|
||||
// expressions for Produce operator.
|
||||
|
@ -182,6 +182,15 @@ class RuleBasedPlanner {
|
||||
DCHECK(!input_op) << "Unexpected operator before CreateIndex";
|
||||
input_op = std::make_unique<plan::CreateIndex>(
|
||||
create_index->label_, create_index->property_);
|
||||
} else if (auto *modify_user =
|
||||
dynamic_cast<query::ModifyUser *>(clause)) {
|
||||
DCHECK(!input_op) << "Unexpected operator before ModifyUser";
|
||||
input_op = std::make_unique<plan::ModifyUser>(
|
||||
modify_user->username_, modify_user->password_,
|
||||
modify_user->is_create_);
|
||||
} else if (auto *drop_user = dynamic_cast<query::DropUser *>(clause)) {
|
||||
DCHECK(!input_op) << "Unexpected operator before DropUser";
|
||||
input_op = std::make_unique<plan::DropUser>(drop_user->usernames_);
|
||||
} else {
|
||||
throw utils::NotYetImplemented("clause conversion to operator(s)");
|
||||
}
|
||||
|
@ -185,6 +185,13 @@ inline void SaveVector(const std::vector<T> &data,
|
||||
}
|
||||
}
|
||||
|
||||
inline void SaveVector(const std::vector<std::string> &data,
|
||||
::capnp::List<::capnp::Text>::Builder *list_builder) {
|
||||
for (size_t i = 0; i < data.size(); ++i) {
|
||||
list_builder->set(i, data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void LoadVector(std::vector<T> *data,
|
||||
const typename ::capnp::List<T>::Reader &list_reader) {
|
||||
@ -193,6 +200,13 @@ inline void LoadVector(std::vector<T> *data,
|
||||
}
|
||||
}
|
||||
|
||||
inline void LoadVector(
|
||||
std::vector<std::string> *data,
|
||||
const typename ::capnp::List<::capnp::Text>::Reader &list_reader) {
|
||||
for (const auto e : list_reader) {
|
||||
data->emplace_back(e); }
|
||||
}
|
||||
|
||||
template <typename TCapnp, typename T>
|
||||
inline void SaveVector(
|
||||
const std::vector<T> &data,
|
||||
|
@ -514,6 +514,16 @@ class PlanPrinter : public query::plan::HierarchicalLogicalOperatorVisitor {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Visit(query::plan::ModifyUser &op) override {
|
||||
WithPrintLn([](auto &out) { out << "* ModifyUser "; });
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Visit(query::plan::DropUser &op) override {
|
||||
WithPrintLn([](auto &out) { out << "* DropUser"; });
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(query::plan::PullRemote &op) override {
|
||||
WithPrintLn([&op](auto &out) {
|
||||
out << "* PullRemote [" << op.plan_id() << "] {";
|
||||
|
@ -1858,4 +1858,72 @@ TYPED_TEST(CypherMainVisitorTest, UnionAll) {
|
||||
ASSERT_FALSE(return_clause->body_.distinct);
|
||||
}
|
||||
|
||||
TYPED_TEST(CypherMainVisitorTest, ModifyUser) {
|
||||
auto check_modify_user = [](std::string input, std::string username,
|
||||
std::experimental::optional<TypedValue> password,
|
||||
bool is_create) {
|
||||
TypeParam ast_generator(input);
|
||||
auto *query = ast_generator.query_;
|
||||
ASSERT_TRUE(query->single_query_);
|
||||
auto *single_query = query->single_query_;
|
||||
ASSERT_EQ(single_query->clauses_.size(), 1U);
|
||||
auto *create_user = dynamic_cast<ModifyUser *>(single_query->clauses_[0]);
|
||||
ASSERT_TRUE(create_user);
|
||||
EXPECT_EQ(create_user->username_, username);
|
||||
if (password) {
|
||||
ASSERT_NE(create_user->password_, nullptr);
|
||||
CheckLiteral(ast_generator.context_, create_user->password_, *password);
|
||||
} else {
|
||||
EXPECT_EQ(create_user->password_, nullptr);
|
||||
}
|
||||
EXPECT_EQ(create_user->is_create_, is_create);
|
||||
};
|
||||
|
||||
check_modify_user("CreaTE UsEr dominik", "dominik",
|
||||
std::experimental::nullopt, true);
|
||||
check_modify_user("CreaTE UsEr dominik WIth PaSSWORD 'spomenik'", "dominik",
|
||||
"spomenik", true);
|
||||
check_modify_user("CreaTE UsEr dominik WIth PaSSWORD NULL", "dominik",
|
||||
TypedValue::Null, true);
|
||||
check_modify_user("AlTeR UsEr dominik", "dominik", std::experimental::nullopt,
|
||||
false);
|
||||
check_modify_user("ALtEr UsEr dominik", "dominik", std::experimental::nullopt,
|
||||
false);
|
||||
check_modify_user("ALtEr UsEr dominik WIth PaSSWORD 'spomenik'", "dominik",
|
||||
"spomenik", false);
|
||||
check_modify_user("ALtEr UsEr dominik WIth PaSSWORD NULL", "dominik",
|
||||
TypedValue::Null, false);
|
||||
EXPECT_THROW(
|
||||
check_modify_user(
|
||||
"CreaTE UsEr dominik WIth PaSSWORD 'spomenik' PaSSwoRD 'u muzeju'",
|
||||
"dominik", "spomenik", true),
|
||||
QueryException);
|
||||
EXPECT_THROW(check_modify_user("CreaTE UsEr dominik WIth PaSSWORD 12345",
|
||||
"dominik", "spomenik", true),
|
||||
SyntaxException);
|
||||
}
|
||||
|
||||
TYPED_TEST(CypherMainVisitorTest, DropUser) {
|
||||
auto check_drop_user = [](std::string input,
|
||||
const std::vector<std::string> &usernames) {
|
||||
TypeParam ast_generator(input);
|
||||
auto *query = ast_generator.query_;
|
||||
ASSERT_TRUE(query->single_query_);
|
||||
auto *single_query = query->single_query_;
|
||||
ASSERT_EQ(single_query->clauses_.size(), 1U);
|
||||
auto *drop_user = dynamic_cast<DropUser *>(single_query->clauses_[0]);
|
||||
ASSERT_TRUE(drop_user);
|
||||
EXPECT_EQ(drop_user->usernames_, usernames);
|
||||
};
|
||||
|
||||
EXPECT_THROW(check_drop_user("DrOp USER", {}), SyntaxException);
|
||||
check_drop_user("DrOP UsEr dominik", {"dominik"});
|
||||
check_drop_user("DrOP USER dominik , spomenik", {"dominik", "spomenik"});
|
||||
EXPECT_THROW(
|
||||
check_drop_user("DrOP USER dominik, , spomenik", {"dominik", "spomenik"}),
|
||||
SyntaxException);
|
||||
check_drop_user("DrOP USER dominik , spomenik , jackie, jackie , johnny",
|
||||
{"dominik", "spomenik", "jackie", "jackie", "johnny"});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -607,3 +607,8 @@ auto GetMerge(AstStorage &storage, Pattern *pattern, OnMatch on_match,
|
||||
storage.Create<query::Reduce>( \
|
||||
storage.Create<query::Identifier>(accumulator), initializer, \
|
||||
storage.Create<query::Identifier>(variable), list, expr)
|
||||
#define CREATE_USER(username, password) \
|
||||
storage.Create<query::ModifyUser>((username), LITERAL(password), true)
|
||||
#define ALTER_USER(username, password) \
|
||||
storage.Create<query::ModifyUser>((username), LITERAL(password), false)
|
||||
#define DROP_USER(usernames) storage.Create<query::DropUser>((usernames))
|
||||
|
@ -29,6 +29,8 @@ namespace query {
|
||||
}
|
||||
} // namespace query
|
||||
|
||||
using std::string_literals::operator""s;
|
||||
|
||||
using namespace query::plan;
|
||||
using query::AstStorage;
|
||||
using query::SingleQuery;
|
||||
@ -69,6 +71,12 @@ class PlanChecker : public HierarchicalLogicalOperatorVisitor {
|
||||
return true; \
|
||||
}
|
||||
|
||||
#define VISIT(TOp) \
|
||||
bool Visit(TOp &op) override { \
|
||||
CheckOp(op); \
|
||||
return true; \
|
||||
}
|
||||
|
||||
PRE_VISIT(CreateNode);
|
||||
PRE_VISIT(CreateExpand);
|
||||
PRE_VISIT(Delete);
|
||||
@ -111,10 +119,7 @@ class PlanChecker : public HierarchicalLogicalOperatorVisitor {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Visit(CreateIndex &op) override {
|
||||
CheckOp(op);
|
||||
return true;
|
||||
}
|
||||
VISIT(CreateIndex);
|
||||
|
||||
PRE_VISIT(PullRemote);
|
||||
|
||||
@ -130,7 +135,11 @@ class PlanChecker : public HierarchicalLogicalOperatorVisitor {
|
||||
}
|
||||
|
||||
PRE_VISIT(PullRemoteOrderBy);
|
||||
|
||||
VISIT(ModifyUser);
|
||||
VISIT(DropUser);
|
||||
#undef PRE_VISIT
|
||||
#undef VISIT
|
||||
|
||||
std::list<BaseOpChecker *> checkers_;
|
||||
|
||||
@ -484,6 +493,37 @@ class Planner {
|
||||
std::unique_ptr<LogicalOperator> plan_;
|
||||
};
|
||||
|
||||
class ExpectModifyUser : public OpChecker<ModifyUser> {
|
||||
public:
|
||||
ExpectModifyUser(std::string username, bool is_create)
|
||||
: username_(username), is_create_(is_create) {}
|
||||
|
||||
void ExpectOp(ModifyUser &modify_user, const SymbolTable &) override {
|
||||
EXPECT_EQ(username_, modify_user.username());
|
||||
// TODO(mtomic): proper password verification
|
||||
EXPECT_NE(dynamic_cast<query::Expression *>(modify_user.password()),
|
||||
nullptr);
|
||||
EXPECT_EQ(is_create_, modify_user.is_create());
|
||||
}
|
||||
|
||||
private:
|
||||
std::string username_;
|
||||
query::Expression *password_;
|
||||
bool is_create_;
|
||||
};
|
||||
|
||||
class ExpectDropUser : public OpChecker<DropUser> {
|
||||
public:
|
||||
ExpectDropUser(std::vector<std::string> usernames) : usernames_(usernames) {}
|
||||
|
||||
void ExpectOp(DropUser &drop_user, const SymbolTable &) override {
|
||||
EXPECT_EQ(usernames_, drop_user.usernames());
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::string> usernames_;
|
||||
};
|
||||
|
||||
class SerializedPlanner {
|
||||
public:
|
||||
SerializedPlanner(std::vector<SingleQueryPart> single_query_parts,
|
||||
@ -2173,6 +2213,43 @@ TYPED_TEST(TestPlanner, ReturnAsteriskOmitsLambdaSymbols) {
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST(TestPlanner, ModifyUser) {
|
||||
{
|
||||
// Test CREATE USER user WITH PASSWORD 'password'
|
||||
database::SingleNode db;
|
||||
database::GraphDbAccessor dba(db);
|
||||
AstStorage storage;
|
||||
QUERY(SINGLE_QUERY(CREATE_USER("user", "password")));
|
||||
CheckPlan<TypeParam>(storage, ExpectModifyUser("user", true));
|
||||
auto expected =
|
||||
ExpectDistributed(MakeCheckers(ExpectModifyUser("user", true)));
|
||||
CheckDistributedPlan<TypeParam>(storage, expected);
|
||||
}
|
||||
{
|
||||
// Test ALTER USER user WITH PASSWORD 'password'
|
||||
database::SingleNode db;
|
||||
database::GraphDbAccessor dba(db);
|
||||
AstStorage storage;
|
||||
QUERY(SINGLE_QUERY(ALTER_USER("user", "password")));
|
||||
CheckPlan<TypeParam>(storage, ExpectModifyUser("user", false));
|
||||
auto expected =
|
||||
ExpectDistributed(MakeCheckers(ExpectModifyUser("user", false)));
|
||||
CheckDistributedPlan<TypeParam>(storage, expected);
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST(TestPlanner, DropUser) {
|
||||
// Test DROP USER user1, user2, user3
|
||||
database::SingleNode db;
|
||||
database::GraphDbAccessor dba(db);
|
||||
AstStorage storage;
|
||||
std::vector<std::string> usernames({"user1", "user2", "user3"});
|
||||
QUERY(SINGLE_QUERY(DROP_USER(usernames)));
|
||||
CheckPlan<TypeParam>(storage, ExpectDropUser(usernames));
|
||||
auto expected = ExpectDistributed(MakeCheckers(ExpectDropUser(usernames)));
|
||||
CheckDistributedPlan<TypeParam>(storage, expected);
|
||||
}
|
||||
|
||||
TYPED_TEST(TestPlanner, DistributedAvg) {
|
||||
// Test MATCH (n) RETURN AVG(n.prop) AS res
|
||||
AstStorage storage;
|
||||
|
Loading…
Reference in New Issue
Block a user