diff --git a/src/query/context.hpp b/src/query/context.hpp
index def5e57ae..8afc3b091 100644
--- a/src/query/context.hpp
+++ b/src/query/context.hpp
@@ -13,6 +13,7 @@ class Context {
   GraphDbAccessor &db_accessor_;
   SymbolTable symbol_table_;
   Parameters parameters_;
+  bool is_query_cached_ = false;
 };
 
 }  // namespace query
diff --git a/src/query/frontend/ast/ast.hpp b/src/query/frontend/ast/ast.hpp
index c10c0f7e8..3cbbccf7a 100644
--- a/src/query/frontend/ast/ast.hpp
+++ b/src/query/frontend/ast/ast.hpp
@@ -1621,80 +1621,6 @@ class CreateIndex : public Clause {
       : Clause(uid), label_(label), property_(property) {}
 };
 
-/// CachedAst is used for storing high level asts.
-///
-/// After query is stripped, parsed and converted to high level ast it can be
-/// stored in this class and new trees can be created by plugging different
-/// literals.
-class CachedAst {
- public:
-  CachedAst(AstTreeStorage storage) : storage_(std::move(storage)) {}
-
-  /// Create new storage by plugging literals and named expessions on theirs
-  /// positions.
-  AstTreeStorage Plug(const Parameters &literals,
-                      const std::unordered_map<int, std::string> &named_exprs) {
-    AstTreeStorage new_ast;
-    storage_.query()->Clone(new_ast);
-    LiteralsPlugger plugger(literals, named_exprs);
-    new_ast.query()->Accept(plugger);
-    return new_ast;
-  }
-
- private:
-  class LiteralsPlugger : public HierarchicalTreeVisitor {
-   public:
-    using HierarchicalTreeVisitor::PreVisit;
-    using typename HierarchicalTreeVisitor::ReturnType;
-    using HierarchicalTreeVisitor::Visit;
-    using HierarchicalTreeVisitor::PostVisit;
-
-    LiteralsPlugger(const Parameters &literals,
-                    const std::unordered_map<int, std::string> &named_exprs)
-        : literals_(literals), named_exprs_(named_exprs) {}
-
-    bool Visit(PrimitiveLiteral &literal) override {
-      if (!literal.value_.IsNull()) {
-        permanent_assert(literal.token_position_ != -1,
-                         "Use AstPlugLiteralsVisitor only on ast created by "
-                         "parsing queries");
-        literal.value_ = literals_.AtTokenPosition(literal.token_position_);
-      }
-      return true;
-    }
-
-    bool PreVisit(NamedExpression &named_expr) override {
-      // We care only about aliased named expressions in return.
-      if (!in_return_ || named_expr.token_position_ == -1) return true;
-      permanent_assert(
-          named_exprs_.count(named_expr.token_position_),
-          "There is no named expression string for needed position");
-      named_expr.name_ = named_exprs_.at(named_expr.token_position_);
-      return true;
-    }
-
-    bool Visit(Identifier &) override { return true; }
-    bool Visit(ParameterLookup &) override { return true; }
-    bool Visit(CreateIndex &) override { return true; }
-
-    bool PreVisit(Return &) override {
-      in_return_ = true;
-      return true;
-    }
-
-    bool PostVisit(Return &) override {
-      in_return_ = false;
-      return true;
-    }
-
-   private:
-    const Parameters &literals_;
-    const std::unordered_map<int, std::string> &named_exprs_;
-    bool in_return_ = false;
-  };
-  AstTreeStorage storage_;
-};
-
 #undef CLONE_BINARY_EXPRESSION
 #undef CLONE_UNARY_EXPRESSION
 }
diff --git a/src/query/frontend/ast/cypher_main_visitor.cpp b/src/query/frontend/ast/cypher_main_visitor.cpp
index 247999a73..ff79ee504 100644
--- a/src/query/frontend/ast/cypher_main_visitor.cpp
+++ b/src/query/frontend/ast/cypher_main_visitor.cpp
@@ -772,11 +772,10 @@ antlrcpp::Any CypherMainVisitor::visitExpression2b(
 
 antlrcpp::Any CypherMainVisitor::visitAtom(CypherParser::AtomContext *ctx) {
   if (ctx->literal()) {
-    return static_cast<Expression *>(
-        ctx->literal()->accept(this).as<BaseLiteral *>());
+    return ctx->literal()->accept(this);
   } else if (ctx->parameter()) {
     return static_cast<Expression *>(
-        ctx->parameter()->accept(this).as<PrimitiveLiteral *>());
+        ctx->parameter()->accept(this).as<ParameterLookup *>());
   } else if (ctx->parenthesizedExpression()) {
     return static_cast<Expression *>(
         ctx->parenthesizedExpression()->accept(this));
@@ -813,34 +812,41 @@ antlrcpp::Any CypherMainVisitor::visitAtom(CypherParser::AtomContext *ctx) {
 
 antlrcpp::Any CypherMainVisitor::visitParameter(
     CypherParser::ParameterContext *ctx) {
-  return storage_.Create<PrimitiveLiteral>(
-      ctx->getText(),  // Not really important since we do parameter
-                       // substitution by token position not by its name.
-                       // Lookup by name is already done in stage before.
-      ctx->getStart()->getTokenIndex());
+  return storage_.Create<ParameterLookup>(ctx->getStart()->getTokenIndex());
 }
 
 antlrcpp::Any CypherMainVisitor::visitLiteral(
     CypherParser::LiteralContext *ctx) {
-  int token_position = ctx->getStart()->getTokenIndex();
-  if (ctx->CYPHERNULL()) {
-    return static_cast<BaseLiteral *>(
-        storage_.Create<PrimitiveLiteral>(TypedValue::Null, token_position));
-  } else if (ctx->StringLiteral()) {
-    return static_cast<BaseLiteral *>(storage_.Create<PrimitiveLiteral>(
-        visitStringLiteral(ctx->StringLiteral()->getText()).as<std::string>(),
-        token_position));
-  } else if (ctx->booleanLiteral()) {
-    return static_cast<BaseLiteral *>(storage_.Create<PrimitiveLiteral>(
-        ctx->booleanLiteral()->accept(this).as<bool>(), token_position));
-  } else if (ctx->numberLiteral()) {
-    return static_cast<BaseLiteral *>(storage_.Create<PrimitiveLiteral>(
-        ctx->numberLiteral()->accept(this).as<TypedValue>(), token_position));
+  if (ctx->CYPHERNULL() || ctx->StringLiteral() || ctx->booleanLiteral() ||
+      ctx->numberLiteral()) {
+    int token_position = ctx->getStart()->getTokenIndex();
+    if (ctx->CYPHERNULL()) {
+      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.
+      return static_cast<Expression *>(
+          storage_.Create<ParameterLookup>(token_position));
+    } else if (ctx->StringLiteral()) {
+      return static_cast<Expression *>(storage_.Create<PrimitiveLiteral>(
+          visitStringLiteral(ctx->StringLiteral()->getText()).as<std::string>(),
+          token_position));
+    } else if (ctx->booleanLiteral()) {
+      return static_cast<Expression *>(storage_.Create<PrimitiveLiteral>(
+          ctx->booleanLiteral()->accept(this).as<bool>(), token_position));
+    } else if (ctx->numberLiteral()) {
+      return static_cast<Expression *>(storage_.Create<PrimitiveLiteral>(
+          ctx->numberLiteral()->accept(this).as<TypedValue>(), token_position));
+    }
+    debug_fail("Expected to handle all cases above");
   } else if (ctx->listLiteral()) {
-    return static_cast<BaseLiteral *>(storage_.Create<ListLiteral>(
+    return static_cast<Expression *>(storage_.Create<ListLiteral>(
         ctx->listLiteral()->accept(this).as<std::vector<Expression *>>()));
   } else {
-    return static_cast<BaseLiteral *>(storage_.Create<MapLiteral>(
+    return static_cast<Expression *>(storage_.Create<MapLiteral>(
         ctx->mapLiteral()
             ->accept(this)
             .as<std::map<std::pair<std::string, GraphDbTypes::Property>,
diff --git a/src/query/frontend/ast/cypher_main_visitor.hpp b/src/query/frontend/ast/cypher_main_visitor.hpp
index c6ca1d50c..7afae403b 100644
--- a/src/query/frontend/ast/cypher_main_visitor.hpp
+++ b/src/query/frontend/ast/cypher_main_visitor.hpp
@@ -426,7 +426,7 @@ class CypherMainVisitor : public antlropencypher::CypherBaseVisitor {
   antlrcpp::Any visitAtom(CypherParser::AtomContext *ctx) override;
 
   /**
-   * @return PrimitiveLiteral*
+   * @return ParameterLookup*
    */
   antlrcpp::Any visitParameter(CypherParser::ParameterContext *ctx) override;
 
@@ -449,7 +449,7 @@ class CypherMainVisitor : public antlropencypher::CypherBaseVisitor {
       CypherParser::FunctionNameContext *ctx) override;
 
   /**
-   * @return BaseLiteral*
+   * @return Expression*
    */
   antlrcpp::Any visitLiteral(CypherParser::LiteralContext *ctx) override;
 
diff --git a/src/query/frontend/semantic/symbol_generator.cpp b/src/query/frontend/semantic/symbol_generator.cpp
index ad8eb20fd..e73f3b810 100644
--- a/src/query/frontend/semantic/symbol_generator.cpp
+++ b/src/query/frontend/semantic/symbol_generator.cpp
@@ -12,8 +12,9 @@
 namespace query {
 
 auto SymbolGenerator::CreateSymbol(const std::string &name, bool user_declared,
-                                   Symbol::Type type) {
-  auto symbol = symbol_table_.CreateSymbol(name, user_declared, type);
+                                   Symbol::Type type, int token_position) {
+  auto symbol =
+      symbol_table_.CreateSymbol(name, user_declared, type, token_position);
   scope_.symbols[name] = symbol;
   return symbol;
 }
@@ -76,7 +77,8 @@ void SymbolGenerator::VisitReturnBody(ReturnBody &body, Where *where) {
     }
     // An improvement would be to infer the type of the expression, so that the
     // new symbol would have a more specific type.
-    symbol_table_[*named_expr] = CreateSymbol(name, true);
+    symbol_table_[*named_expr] = CreateSymbol(name, true, Symbol::Type::Any,
+                                              named_expr->token_position_);
   }
   scope_.in_order_by = true;
   for (const auto &order_pair : body.order_by) {
diff --git a/src/query/frontend/semantic/symbol_generator.hpp b/src/query/frontend/semantic/symbol_generator.hpp
index 0b6791375..996e8d183 100644
--- a/src/query/frontend/semantic/symbol_generator.hpp
+++ b/src/query/frontend/semantic/symbol_generator.hpp
@@ -106,7 +106,8 @@ class SymbolGenerator : public HierarchicalTreeVisitor {
   // Returns a freshly generated symbol. Previous mapping of the same name to a
   // different symbol is replaced with the new one.
   auto CreateSymbol(const std::string &name, bool user_declared,
-                    Symbol::Type type = Symbol::Type::Any);
+                    Symbol::Type type = Symbol::Type::Any,
+                    int token_position = -1);
 
   // Returns the symbol by name. If the mapping already exists, checks if the
   // types match. Otherwise, returns a new symbol.
diff --git a/src/query/frontend/semantic/symbol_table.hpp b/src/query/frontend/semantic/symbol_table.hpp
index 84e63242c..27671a810 100644
--- a/src/query/frontend/semantic/symbol_table.hpp
+++ b/src/query/frontend/semantic/symbol_table.hpp
@@ -21,11 +21,12 @@ class Symbol {
 
   Symbol() {}
   Symbol(const std::string &name, int position, bool user_declared,
-         Type type = Type::Any)
+         Type type = Type::Any, int token_position = -1)
       : name_(name),
         position_(position),
         user_declared_(user_declared),
-        type_(type) {}
+        type_(type),
+        token_position_(token_position) {}
 
   bool operator==(const Symbol &other) const {
     return position_ == other.position_ && name_ == other.name_ &&
@@ -37,20 +38,23 @@ class Symbol {
   int position() const { return position_; }
   Type type() const { return type_; }
   bool user_declared() const { return user_declared_; }
+  int token_position() const { return token_position_; }
 
  private:
   std::string name_;
   int position_;
   bool user_declared_ = true;
   Type type_ = Type::Any;
+  int token_position_ = -1;
 };
 
 class SymbolTable {
  public:
   Symbol CreateSymbol(const std::string &name, bool user_declared,
-                      Symbol::Type type = Symbol::Type::Any) {
+                      Symbol::Type type = Symbol::Type::Any,
+                      int token_position = -1) {
     int position = position_++;
-    return Symbol(name, position, user_declared, type);
+    return Symbol(name, position, user_declared, type, token_position);
   }
 
   auto &operator[](const Tree &tree) { return table_[tree.uid()]; }
diff --git a/src/query/interpreter.hpp b/src/query/interpreter.hpp
index 73a20d601..efc955097 100644
--- a/src/query/interpreter.hpp
+++ b/src/query/interpreter.hpp
@@ -35,11 +35,14 @@ class Interpreter {
                  const std::map<std::string, TypedValue> &params) {
     utils::Timer frontend_timer;
     Context ctx(db_accessor);
+    ctx.is_query_cached_ = FLAGS_ast_cache;
     std::map<std::string, TypedValue> summary;
 
+    // query -> stripped query
+    StrippedQuery stripped(query);
     // stripped query -> high level tree
     AstTreeStorage ast_storage = [&]() {
-      if (!FLAGS_ast_cache) {
+      if (!ctx.is_query_cached_) {
         // This is totally fine, since we don't really expect anyone to turn off
         // the cache.
         if (!params.empty()) {
@@ -61,12 +64,9 @@ class Interpreter {
         return std::move(visitor.storage());
       }
 
-      // query -> stripped query
-      StrippedQuery stripped(query);
-
       auto ast_cache_accessor = ast_cache_.access();
-      auto it = ast_cache_accessor.find(stripped.hash());
-      if (it == ast_cache_accessor.end()) {
+      auto ast_it = ast_cache_accessor.find(stripped.hash());
+      if (ast_it == ast_cache_accessor.end()) {
         // stripped query -> AST
         auto parser = [&] {
           // Be careful about unlocking since parser can throw.
@@ -81,10 +81,9 @@ class Interpreter {
         visitor.visit(low_level_tree);
 
         // Cache it.
-        it = ast_cache_accessor
-                 .insert(stripped.hash(),
-                         CachedAst(std::move(visitor.storage())))
-                 .first;
+        ast_it = ast_cache_accessor
+                     .insert(stripped.hash(), std::move(visitor.storage()))
+                     .first;
       }
 
       // Update context with provided parameters.
@@ -98,8 +97,9 @@ class Interpreter {
         ctx.parameters_.Add(param_pair.first, param_it->second);
       }
 
-      // Plug literals, parameters and named expressions.
-      return it->second.Plug(ctx.parameters_, stripped.named_expressions());
+      AstTreeStorage new_ast;
+      ast_it->second.query()->Clone(new_ast);
+      return new_ast;
     }();
     auto frontend_time = frontend_timer.Elapsed();
 
@@ -146,7 +146,16 @@ class Interpreter {
       // clause, so stream out the results.
 
       // generate header
-      for (const auto &symbol : output_symbols) header.push_back(symbol.name());
+      for (const auto &symbol : output_symbols) {
+        // When the symbol is aliased or expanded from '*' (inside RETURN or
+        // WITH), then there is no token position, so use symbol name.
+        // Otherwise, find the name from stripped query.
+        if (symbol.token_position() == -1)
+          header.push_back(symbol.name());
+        else
+          header.push_back(
+              stripped.named_expressions().at(symbol.token_position()));
+      }
       stream.Header(header);
 
       // stream out results
@@ -192,7 +201,7 @@ class Interpreter {
   }
 
  private:
-  ConcurrentMap<HashType, CachedAst> ast_cache_;
+  ConcurrentMap<HashType, AstTreeStorage> ast_cache_;
   // Antlr has singleton instance that is shared between threads. It is
   // protected by locks inside of antlr. Unfortunately, they are not protected
   // in a very good way. Once we have antlr version without race conditions we
diff --git a/src/query/plan/rule_based_planner.cpp b/src/query/plan/rule_based_planner.cpp
index e410064cf..97e46653a 100644
--- a/src/query/plan/rule_based_planner.cpp
+++ b/src/query/plan/rule_based_planner.cpp
@@ -377,8 +377,15 @@ class ReturnBodyContext : public HierarchicalTreeVisitor {
     return true;
   }
 
-  bool Visit(ParameterLookup &) override { return true; }
-  bool Visit(query::CreateIndex &) override { return true; }
+  bool Visit(ParameterLookup &) override {
+    has_aggregation_.emplace_back(false);
+    return true;
+  }
+
+  bool Visit(query::CreateIndex &) 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
diff --git a/tests/unit/cypher_main_visitor.cpp b/tests/unit/cypher_main_visitor.cpp
index 9c5d5b7de..f35da20a2 100644
--- a/tests/unit/cypher_main_visitor.cpp
+++ b/tests/unit/cypher_main_visitor.cpp
@@ -92,12 +92,15 @@ class CachedAstGenerator : public Base {
   CachedAstGenerator(const std::string &query)
       : Base(query),
         storage_([&]() {
+          context_.is_query_cached_ = true;
           StrippedQuery stripped(query_string_);
+          context_.parameters_ = stripped.literals();
           ::frontend::opencypher::Parser parser(stripped.query());
           CypherMainVisitor visitor(context_);
           visitor.visit(parser.tree());
-          CachedAst cached(std::move(visitor.storage()));
-          return cached.Plug(stripped.literals(), stripped.named_expressions());
+          AstTreeStorage new_ast;
+          visitor.storage().query()->Clone(new_ast);
+          return new_ast;
         }()),
         query_(storage_.query()) {}
 
@@ -203,15 +206,43 @@ TYPED_TEST(CypherMainVisitorTest, ReturnDistinct) {
   ASSERT_TRUE(return_clause->body_.distinct);
 }
 
+TypedValue LiteralValue(const Context &context, Expression *expression) {
+  if (context.is_query_cached_) {
+    auto *param_lookup = dynamic_cast<ParameterLookup *>(expression);
+    return context.parameters_.AtTokenPosition(param_lookup->token_position_);
+  } else {
+    auto *literal = dynamic_cast<PrimitiveLiteral *>(expression);
+    return literal->value_;
+  }
+}
+
+void CheckLiteral(const Context &context, Expression *expression,
+                  const TypedValue &expected,
+                  const std::experimental::optional<int> &token_position =
+                      std::experimental::nullopt) {
+  TypedValue value;
+  if (!expected.IsNull() && context.is_query_cached_) {
+    auto *param_lookup = dynamic_cast<ParameterLookup *>(expression);
+    ASSERT_TRUE(param_lookup);
+    if (token_position)
+      EXPECT_EQ(param_lookup->token_position_, *token_position);
+    value = context.parameters_.AtTokenPosition(param_lookup->token_position_);
+  } else {
+    auto *literal = dynamic_cast<PrimitiveLiteral *>(expression);
+    ASSERT_TRUE(literal);
+    if (token_position) ASSERT_EQ(literal->token_position_, *token_position);
+    value = literal->value_;
+  }
+  EXPECT_TRUE(TypedValue::BoolEqual{}(value, expected));
+}
+
 TYPED_TEST(CypherMainVisitorTest, ReturnLimit) {
   TypeParam ast_generator("RETURN x LIMIT 5");
   auto *query = ast_generator.query_;
   ASSERT_EQ(query->clauses_.size(), 1U);
   auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
   ASSERT_TRUE(return_clause->body_.limit);
-  auto *literal = dynamic_cast<PrimitiveLiteral *>(return_clause->body_.limit);
-  ASSERT_TRUE(literal);
-  ASSERT_EQ(literal->value_.Value<int64_t>(), 5);
+  CheckLiteral(ast_generator.context_, return_clause->body_.limit, 5);
 }
 
 TYPED_TEST(CypherMainVisitorTest, ReturnSkip) {
@@ -220,9 +251,7 @@ TYPED_TEST(CypherMainVisitorTest, ReturnSkip) {
   ASSERT_EQ(query->clauses_.size(), 1U);
   auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
   ASSERT_TRUE(return_clause->body_.skip);
-  auto *literal = dynamic_cast<PrimitiveLiteral *>(return_clause->body_.skip);
-  ASSERT_TRUE(literal);
-  ASSERT_EQ(literal->value_.Value<int64_t>(), 5);
+  CheckLiteral(ast_generator.context_, return_clause->body_.skip, 5);
 }
 
 TYPED_TEST(CypherMainVisitorTest, ReturnOrderBy) {
@@ -264,11 +293,8 @@ TYPED_TEST(CypherMainVisitorTest, IntegerLiteral) {
   TypeParam ast_generator("RETURN 42");
   auto *query = ast_generator.query_;
   auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
-  auto *literal = dynamic_cast<PrimitiveLiteral *>(
-      return_clause->body_.named_expressions[0]->expression_);
-  ASSERT_TRUE(literal);
-  EXPECT_EQ(literal->value_.Value<int64_t>(), 42);
-  EXPECT_EQ(literal->token_position_, 2);
+  CheckLiteral(ast_generator.context_,
+               return_clause->body_.named_expressions[0]->expression_, 42, 2);
 }
 
 TYPED_TEST(CypherMainVisitorTest, IntegerLiteralTooLarge) {
@@ -280,43 +306,34 @@ TYPED_TEST(CypherMainVisitorTest, BooleanLiteralTrue) {
   TypeParam ast_generator("RETURN TrUe");
   auto *query = ast_generator.query_;
   auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
-  auto *literal = dynamic_cast<PrimitiveLiteral *>(
-      return_clause->body_.named_expressions[0]->expression_);
-  ASSERT_TRUE(literal);
-  EXPECT_EQ(literal->value_.Value<bool>(), true);
-  EXPECT_EQ(literal->token_position_, 2);
+  CheckLiteral(ast_generator.context_,
+               return_clause->body_.named_expressions[0]->expression_, true, 2);
 }
 
 TYPED_TEST(CypherMainVisitorTest, BooleanLiteralFalse) {
   TypeParam ast_generator("RETURN faLSE");
   auto *query = ast_generator.query_;
   auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
-  auto *literal = dynamic_cast<PrimitiveLiteral *>(
-      return_clause->body_.named_expressions[0]->expression_);
-  ASSERT_TRUE(literal);
-  EXPECT_EQ(literal->value_.Value<bool>(), false);
-  EXPECT_EQ(literal->token_position_, 2);
+  CheckLiteral(ast_generator.context_,
+               return_clause->body_.named_expressions[0]->expression_, false,
+               2);
 }
 
 TYPED_TEST(CypherMainVisitorTest, NullLiteral) {
   TypeParam ast_generator("RETURN nULl");
   auto *query = ast_generator.query_;
   auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
-  auto *literal = dynamic_cast<PrimitiveLiteral *>(
-      return_clause->body_.named_expressions[0]->expression_);
-  ASSERT_TRUE(literal);
-  EXPECT_EQ(literal->value_.type(), TypedValue::Type::Null);
-  EXPECT_EQ(literal->token_position_, 2);
+  CheckLiteral(ast_generator.context_,
+               return_clause->body_.named_expressions[0]->expression_,
+               TypedValue::Null, 2);
 }
 
 TYPED_TEST(CypherMainVisitorTest, ParenthesizedExpression) {
   TypeParam ast_generator("RETURN (2)");
   auto *query = ast_generator.query_;
   auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
-  auto *literal = dynamic_cast<PrimitiveLiteral *>(
-      return_clause->body_.named_expressions[0]->expression_);
-  ASSERT_TRUE(literal);
-  ASSERT_EQ(literal->value_.Value<int64_t>(), 2);
+  CheckLiteral(ast_generator.context_,
+               return_clause->body_.named_expressions[0]->expression_, 2);
 }
 
 TYPED_TEST(CypherMainVisitorTest, OrOperator) {
@@ -329,12 +346,8 @@ TYPED_TEST(CypherMainVisitorTest, OrOperator) {
   ASSERT_TRUE(or_operator2);
   auto *or_operator1 = dynamic_cast<OrOperator *>(or_operator2->expression1_);
   ASSERT_TRUE(or_operator1);
-  auto *operand1 = dynamic_cast<PrimitiveLiteral *>(or_operator1->expression1_);
-  ASSERT_TRUE(operand1);
-  ASSERT_EQ(operand1->value_.Value<bool>(), true);
-  auto *operand2 = dynamic_cast<PrimitiveLiteral *>(or_operator1->expression2_);
-  ASSERT_TRUE(operand2);
-  ASSERT_EQ(operand2->value_.Value<bool>(), false);
+  CheckLiteral(ast_generator.context_, or_operator1->expression1_, true);
+  CheckLiteral(ast_generator.context_, or_operator1->expression2_, false);
   auto *operand3 = dynamic_cast<Identifier *>(or_operator2->expression2_);
   ASSERT_TRUE(operand3);
   ASSERT_EQ(operand3->name_, "n");
@@ -346,12 +359,8 @@ TYPED_TEST(CypherMainVisitorTest, XorOperator) {
   auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
   auto *xor_operator = dynamic_cast<XorOperator *>(
       return_clause->body_.named_expressions[0]->expression_);
-  auto *operand1 = dynamic_cast<PrimitiveLiteral *>(xor_operator->expression1_);
-  ASSERT_TRUE(operand1);
-  ASSERT_EQ(operand1->value_.Value<bool>(), true);
-  auto *operand2 = dynamic_cast<PrimitiveLiteral *>(xor_operator->expression2_);
-  ASSERT_TRUE(operand2);
-  ASSERT_EQ(operand2->value_.Value<bool>(), false);
+  CheckLiteral(ast_generator.context_, xor_operator->expression1_, true);
+  CheckLiteral(ast_generator.context_, xor_operator->expression2_, false);
 }
 
 TYPED_TEST(CypherMainVisitorTest, AndOperator) {
@@ -360,12 +369,8 @@ TYPED_TEST(CypherMainVisitorTest, AndOperator) {
   auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
   auto *and_operator = dynamic_cast<AndOperator *>(
       return_clause->body_.named_expressions[0]->expression_);
-  auto *operand1 = dynamic_cast<PrimitiveLiteral *>(and_operator->expression1_);
-  ASSERT_TRUE(operand1);
-  ASSERT_EQ(operand1->value_.Value<bool>(), true);
-  auto *operand2 = dynamic_cast<PrimitiveLiteral *>(and_operator->expression2_);
-  ASSERT_TRUE(operand2);
-  ASSERT_EQ(operand2->value_.Value<bool>(), false);
+  CheckLiteral(ast_generator.context_, and_operator->expression1_, true);
+  CheckLiteral(ast_generator.context_, and_operator->expression2_, false);
 }
 
 TYPED_TEST(CypherMainVisitorTest, AdditionSubtractionOperators) {
@@ -378,18 +383,9 @@ TYPED_TEST(CypherMainVisitorTest, AdditionSubtractionOperators) {
   auto *subtraction_operator =
       dynamic_cast<SubtractionOperator *>(addition_operator->expression1_);
   ASSERT_TRUE(subtraction_operator);
-  auto *operand1 =
-      dynamic_cast<PrimitiveLiteral *>(subtraction_operator->expression1_);
-  ASSERT_TRUE(operand1);
-  ASSERT_EQ(operand1->value_.Value<int64_t>(), 1);
-  auto *operand2 =
-      dynamic_cast<PrimitiveLiteral *>(subtraction_operator->expression2_);
-  ASSERT_TRUE(operand2);
-  ASSERT_EQ(operand2->value_.Value<int64_t>(), 2);
-  auto *operand3 =
-      dynamic_cast<PrimitiveLiteral *>(addition_operator->expression2_);
-  ASSERT_TRUE(operand3);
-  ASSERT_EQ(operand3->value_.Value<int64_t>(), 3);
+  CheckLiteral(ast_generator.context_, subtraction_operator->expression1_, 1);
+  CheckLiteral(ast_generator.context_, subtraction_operator->expression2_, 2);
+  CheckLiteral(ast_generator.context_, addition_operator->expression2_, 3);
 }
 
 TYPED_TEST(CypherMainVisitorTest, MulitplicationOperator) {
@@ -398,14 +394,8 @@ TYPED_TEST(CypherMainVisitorTest, MulitplicationOperator) {
   auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
   auto *mult_operator = dynamic_cast<MultiplicationOperator *>(
       return_clause->body_.named_expressions[0]->expression_);
-  auto *operand1 =
-      dynamic_cast<PrimitiveLiteral *>(mult_operator->expression1_);
-  ASSERT_TRUE(operand1);
-  ASSERT_EQ(operand1->value_.Value<int64_t>(), 2);
-  auto *operand2 =
-      dynamic_cast<PrimitiveLiteral *>(mult_operator->expression2_);
-  ASSERT_TRUE(operand2);
-  ASSERT_EQ(operand2->value_.Value<int64_t>(), 3);
+  CheckLiteral(ast_generator.context_, mult_operator->expression1_, 2);
+  CheckLiteral(ast_generator.context_, mult_operator->expression2_, 3);
 }
 
 TYPED_TEST(CypherMainVisitorTest, DivisionOperator) {
@@ -414,12 +404,8 @@ TYPED_TEST(CypherMainVisitorTest, DivisionOperator) {
   auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
   auto *div_operator = dynamic_cast<DivisionOperator *>(
       return_clause->body_.named_expressions[0]->expression_);
-  auto *operand1 = dynamic_cast<PrimitiveLiteral *>(div_operator->expression1_);
-  ASSERT_TRUE(operand1);
-  ASSERT_EQ(operand1->value_.Value<int64_t>(), 2);
-  auto *operand2 = dynamic_cast<PrimitiveLiteral *>(div_operator->expression2_);
-  ASSERT_TRUE(operand2);
-  ASSERT_EQ(operand2->value_.Value<int64_t>(), 3);
+  CheckLiteral(ast_generator.context_, div_operator->expression1_, 2);
+  CheckLiteral(ast_generator.context_, div_operator->expression2_, 3);
 }
 
 TYPED_TEST(CypherMainVisitorTest, ModOperator) {
@@ -428,27 +414,19 @@ TYPED_TEST(CypherMainVisitorTest, ModOperator) {
   auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
   auto *mod_operator = dynamic_cast<ModOperator *>(
       return_clause->body_.named_expressions[0]->expression_);
-  auto *operand1 = dynamic_cast<PrimitiveLiteral *>(mod_operator->expression1_);
-  ASSERT_TRUE(operand1);
-  ASSERT_EQ(operand1->value_.Value<int64_t>(), 2);
-  auto *operand2 = dynamic_cast<PrimitiveLiteral *>(mod_operator->expression2_);
-  ASSERT_TRUE(operand2);
-  ASSERT_EQ(operand2->value_.Value<int64_t>(), 3);
+  CheckLiteral(ast_generator.context_, mod_operator->expression1_, 2);
+  CheckLiteral(ast_generator.context_, mod_operator->expression2_, 3);
 }
 
-#define CHECK_COMPARISON(TYPE, VALUE1, VALUE2)                             \
-  do {                                                                     \
-    auto *and_operator = dynamic_cast<AndOperator *>(_operator);           \
-    ASSERT_TRUE(and_operator);                                             \
-    _operator = and_operator->expression1_;                                \
-    auto *cmp_operator = dynamic_cast<TYPE *>(and_operator->expression2_); \
-    ASSERT_TRUE(cmp_operator);                                             \
-    auto *operand1 =                                                       \
-        dynamic_cast<PrimitiveLiteral *>(cmp_operator->expression1_);      \
-    ASSERT_EQ(operand1->value_.Value<int64_t>(), VALUE1);                  \
-    auto *operand2 =                                                       \
-        dynamic_cast<PrimitiveLiteral *>(cmp_operator->expression2_);      \
-    ASSERT_EQ(operand2->value_.Value<int64_t>(), VALUE2);                  \
+#define CHECK_COMPARISON(TYPE, VALUE1, VALUE2)                                \
+  do {                                                                        \
+    auto *and_operator = dynamic_cast<AndOperator *>(_operator);              \
+    ASSERT_TRUE(and_operator);                                                \
+    _operator = and_operator->expression1_;                                   \
+    auto *cmp_operator = dynamic_cast<TYPE *>(and_operator->expression2_);    \
+    ASSERT_TRUE(cmp_operator);                                                \
+    CheckLiteral(ast_generator.context_, cmp_operator->expression1_, VALUE1); \
+    CheckLiteral(ast_generator.context_, cmp_operator->expression2_, VALUE2); \
   } while (0)
 
 TYPED_TEST(CypherMainVisitorTest, ComparisonOperators) {
@@ -465,10 +443,8 @@ TYPED_TEST(CypherMainVisitorTest, ComparisonOperators) {
   CHECK_COMPARISON(NotEqualOperator, 3, 4);
   auto *cmp_operator = dynamic_cast<EqualOperator *>(_operator);
   ASSERT_TRUE(cmp_operator);
-  auto *operand1 = dynamic_cast<PrimitiveLiteral *>(cmp_operator->expression1_);
-  ASSERT_EQ(operand1->value_.Value<int64_t>(), 2);
-  auto *operand2 = dynamic_cast<PrimitiveLiteral *>(cmp_operator->expression2_);
-  ASSERT_EQ(operand2->value_.Value<int64_t>(), 3);
+  CheckLiteral(ast_generator.context_, cmp_operator->expression1_, 2);
+  CheckLiteral(ast_generator.context_, cmp_operator->expression2_, 3);
 }
 
 #undef CHECK_COMPARISON
@@ -482,8 +458,7 @@ TYPED_TEST(CypherMainVisitorTest, ListIndexing) {
   ASSERT_TRUE(list_index_op);
   auto *list = dynamic_cast<ListLiteral *>(list_index_op->expression1_);
   EXPECT_TRUE(list);
-  auto *index = dynamic_cast<PrimitiveLiteral *>(list_index_op->expression2_);
-  ASSERT_EQ(index->value_.Value<int64_t>(), 2);
+  CheckLiteral(ast_generator.context_, list_index_op->expression2_, 2);
 }
 
 TYPED_TEST(CypherMainVisitorTest, ListSlicingOperatorNoBounds) {
@@ -500,9 +475,7 @@ TYPED_TEST(CypherMainVisitorTest, ListSlicingOperator) {
   auto *list = dynamic_cast<ListLiteral *>(list_slicing_op->list_);
   EXPECT_TRUE(list);
   EXPECT_FALSE(list_slicing_op->lower_bound_);
-  auto *upper_bound =
-      dynamic_cast<PrimitiveLiteral *>(list_slicing_op->upper_bound_);
-  EXPECT_EQ(upper_bound->value_.Value<int64_t>(), 2);
+  CheckLiteral(ast_generator.context_, list_slicing_op->upper_bound_, 2);
 }
 
 TYPED_TEST(CypherMainVisitorTest, InListOperator) {
@@ -512,10 +485,7 @@ TYPED_TEST(CypherMainVisitorTest, InListOperator) {
   auto *in_list_operator = dynamic_cast<InListOperator *>(
       return_clause->body_.named_expressions[0]->expression_);
   ASSERT_TRUE(in_list_operator);
-  auto *literal =
-      dynamic_cast<PrimitiveLiteral *>(in_list_operator->expression1_);
-  ASSERT_TRUE(literal);
-  ASSERT_EQ(literal->value_.Value<int64_t>(), 5);
+  CheckLiteral(ast_generator.context_, in_list_operator->expression1_, 5);
   auto *list = dynamic_cast<ListLiteral *>(in_list_operator->expression2_);
   ASSERT_TRUE(list);
 }
@@ -527,18 +497,13 @@ TYPED_TEST(CypherMainVisitorTest, InWithListIndexing) {
   auto *in_list_operator = dynamic_cast<InListOperator *>(
       return_clause->body_.named_expressions[0]->expression_);
   ASSERT_TRUE(in_list_operator);
-  auto *literal =
-      dynamic_cast<PrimitiveLiteral *>(in_list_operator->expression1_);
-  ASSERT_TRUE(literal);
-  EXPECT_EQ(literal->value_.Value<int64_t>(), 1);
+  CheckLiteral(ast_generator.context_, in_list_operator->expression1_, 1);
   auto *list_indexing =
       dynamic_cast<ListMapIndexingOperator *>(in_list_operator->expression2_);
   ASSERT_TRUE(list_indexing);
   auto *list = dynamic_cast<ListLiteral *>(list_indexing->expression1_);
   EXPECT_TRUE(list);
-  auto *list_index =
-      dynamic_cast<PrimitiveLiteral *>(list_indexing->expression2_);
-  EXPECT_TRUE(list_index);
+  CheckLiteral(ast_generator.context_, list_indexing->expression2_, 0);
 }
 
 TYPED_TEST(CypherMainVisitorTest, CaseGenericForm) {
@@ -551,24 +516,16 @@ TYPED_TEST(CypherMainVisitorTest, CaseGenericForm) {
   ASSERT_TRUE(if_operator);
   auto *condition = dynamic_cast<LessOperator *>(if_operator->condition_);
   ASSERT_TRUE(condition);
-  auto *then_expression =
-      dynamic_cast<PrimitiveLiteral *>(if_operator->then_expression_);
-  ASSERT_TRUE(then_expression);
-  ASSERT_EQ(then_expression->value_.Value<int64_t>(), 1);
+  CheckLiteral(ast_generator.context_, if_operator->then_expression_, 1);
 
   auto *if_operator2 =
       dynamic_cast<IfOperator *>(if_operator->else_expression_);
   ASSERT_TRUE(if_operator2);
   auto *condition2 = dynamic_cast<GreaterOperator *>(if_operator2->condition_);
   ASSERT_TRUE(condition2);
-  auto *then_expression2 =
-      dynamic_cast<PrimitiveLiteral *>(if_operator2->then_expression_);
-  ASSERT_TRUE(then_expression2);
-  ASSERT_EQ(then_expression2->value_.Value<int64_t>(), 2);
-  auto *else_expression2 =
-      dynamic_cast<PrimitiveLiteral *>(if_operator2->else_expression_);
-  ASSERT_TRUE(else_expression2);
-  ASSERT_TRUE(else_expression2->value_.IsNull());
+  CheckLiteral(ast_generator.context_, if_operator2->then_expression_, 2);
+  CheckLiteral(ast_generator.context_, if_operator2->else_expression_,
+               TypedValue::Null);
 }
 
 TYPED_TEST(CypherMainVisitorTest, CaseGenericFormElse) {
@@ -579,13 +536,8 @@ TYPED_TEST(CypherMainVisitorTest, CaseGenericFormElse) {
       return_clause->body_.named_expressions[0]->expression_);
   auto *condition = dynamic_cast<LessOperator *>(if_operator->condition_);
   ASSERT_TRUE(condition);
-  auto *then_expression =
-      dynamic_cast<PrimitiveLiteral *>(if_operator->then_expression_);
-  ASSERT_EQ(then_expression->value_.Value<int64_t>(), 1);
-  auto *else_expression =
-      dynamic_cast<PrimitiveLiteral *>(if_operator->else_expression_);
-  ASSERT_TRUE(else_expression);
-  ASSERT_EQ(else_expression->value_.Value<int64_t>(), 2);
+  CheckLiteral(ast_generator.context_, if_operator->then_expression_, 1);
+  CheckLiteral(ast_generator.context_, if_operator->else_expression_, 2);
 }
 
 TYPED_TEST(CypherMainVisitorTest, CaseSimpleForm) {
@@ -596,19 +548,11 @@ TYPED_TEST(CypherMainVisitorTest, CaseSimpleForm) {
       return_clause->body_.named_expressions[0]->expression_);
   auto *condition = dynamic_cast<EqualOperator *>(if_operator->condition_);
   ASSERT_TRUE(condition);
-  auto *expr1 = dynamic_cast<PrimitiveLiteral *>(condition->expression1_);
-  ASSERT_TRUE(expr1);
-  ASSERT_EQ(expr1->value_.Value<int64_t>(), 5);
-  auto *expr2 = dynamic_cast<PrimitiveLiteral *>(condition->expression2_);
-  ASSERT_TRUE(expr2);
-  ASSERT_EQ(expr2->value_.Value<int64_t>(), 10);
-  auto *then_expression =
-      dynamic_cast<PrimitiveLiteral *>(if_operator->then_expression_);
-  ASSERT_EQ(then_expression->value_.Value<int64_t>(), 1);
-  auto *else_expression =
-      dynamic_cast<PrimitiveLiteral *>(if_operator->else_expression_);
-  ASSERT_TRUE(else_expression);
-  ASSERT_TRUE(else_expression->value_.IsNull());
+  CheckLiteral(ast_generator.context_, condition->expression1_, 5);
+  CheckLiteral(ast_generator.context_, condition->expression2_, 10);
+  CheckLiteral(ast_generator.context_, if_operator->then_expression_, 1);
+  CheckLiteral(ast_generator.context_, if_operator->else_expression_,
+               TypedValue::Null);
 }
 
 TYPED_TEST(CypherMainVisitorTest, IsNull) {
@@ -617,10 +561,7 @@ TYPED_TEST(CypherMainVisitorTest, IsNull) {
   auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
   auto *is_type_operator = dynamic_cast<IsNullOperator *>(
       return_clause->body_.named_expressions[0]->expression_);
-  auto *operand1 =
-      dynamic_cast<PrimitiveLiteral *>(is_type_operator->expression_);
-  ASSERT_TRUE(operand1);
-  ASSERT_EQ(operand1->value_.Value<int64_t>(), 2);
+  CheckLiteral(ast_generator.context_, is_type_operator->expression_, 2);
 }
 
 TYPED_TEST(CypherMainVisitorTest, IsNotNull) {
@@ -631,10 +572,7 @@ TYPED_TEST(CypherMainVisitorTest, IsNotNull) {
       return_clause->body_.named_expressions[0]->expression_);
   auto *is_type_operator =
       dynamic_cast<IsNullOperator *>(not_operator->expression_);
-  auto *operand1 =
-      dynamic_cast<PrimitiveLiteral *>(is_type_operator->expression_);
-  ASSERT_TRUE(operand1);
-  ASSERT_EQ(operand1->value_.Value<int64_t>(), 2);
+  CheckLiteral(ast_generator.context_, is_type_operator->expression_, 2);
 }
 
 TYPED_TEST(CypherMainVisitorTest, NotOperator) {
@@ -643,9 +581,7 @@ TYPED_TEST(CypherMainVisitorTest, NotOperator) {
   auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
   auto *not_operator = dynamic_cast<NotOperator *>(
       return_clause->body_.named_expressions[0]->expression_);
-  auto *operand = dynamic_cast<PrimitiveLiteral *>(not_operator->expression_);
-  ASSERT_TRUE(operand);
-  ASSERT_EQ(operand->value_.Value<bool>(), true);
+  CheckLiteral(ast_generator.context_, not_operator->expression_, true);
 }
 
 TYPED_TEST(CypherMainVisitorTest, UnaryMinusPlusOperators) {
@@ -658,10 +594,7 @@ TYPED_TEST(CypherMainVisitorTest, UnaryMinusPlusOperators) {
   auto *unary_plus_operator =
       dynamic_cast<UnaryPlusOperator *>(unary_minus_operator->expression_);
   ASSERT_TRUE(unary_plus_operator);
-  auto *operand =
-      dynamic_cast<PrimitiveLiteral *>(unary_plus_operator->expression_);
-  ASSERT_TRUE(operand);
-  ASSERT_EQ(operand->value_.Value<int64_t>(), 5);
+  CheckLiteral(ast_generator.context_, unary_plus_operator->expression_, 5);
 }
 
 TYPED_TEST(CypherMainVisitorTest, Aggregation) {
@@ -713,44 +646,36 @@ TYPED_TEST(CypherMainVisitorTest, StringLiteralDoubleQuotes) {
   TypeParam ast_generator("RETURN \"mi'rko\"");
   auto *query = ast_generator.query_;
   auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
-  auto *literal = dynamic_cast<PrimitiveLiteral *>(
-      return_clause->body_.named_expressions[0]->expression_);
-  ASSERT_TRUE(literal);
-  EXPECT_EQ(literal->value_.Value<std::string>(), "mi'rko");
-  EXPECT_EQ(literal->token_position_, 2);
+  CheckLiteral(ast_generator.context_,
+               return_clause->body_.named_expressions[0]->expression_, "mi'rko",
+               2);
 }
 
 TYPED_TEST(CypherMainVisitorTest, StringLiteralSingleQuotes) {
   TypeParam ast_generator("RETURN 'mi\"rko'");
   auto *query = ast_generator.query_;
   auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
-  auto *literal = dynamic_cast<PrimitiveLiteral *>(
-      return_clause->body_.named_expressions[0]->expression_);
-  ASSERT_TRUE(literal);
-  EXPECT_EQ(literal->value_.Value<std::string>(), "mi\"rko");
-  EXPECT_EQ(literal->token_position_, 2);
+  CheckLiteral(ast_generator.context_,
+               return_clause->body_.named_expressions[0]->expression_,
+               "mi\"rko", 2);
 }
 
 TYPED_TEST(CypherMainVisitorTest, StringLiteralEscapedChars) {
   TypeParam ast_generator("RETURN '\\\\\\'\\\"\\b\\B\\f\\F\\n\\N\\r\\R\\t\\T'");
   auto *query = ast_generator.query_;
   auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
-  auto *literal = dynamic_cast<PrimitiveLiteral *>(
-      return_clause->body_.named_expressions[0]->expression_);
-  ASSERT_TRUE(literal);
-  EXPECT_EQ(literal->value_.Value<std::string>(), "\\'\"\b\b\f\f\n\n\r\r\t\t");
-  EXPECT_EQ(literal->token_position_, 2);
+  CheckLiteral(ast_generator.context_,
+               return_clause->body_.named_expressions[0]->expression_,
+               "\\'\"\b\b\f\f\n\n\r\r\t\t", 2);
 }
 
 TYPED_TEST(CypherMainVisitorTest, StringLiteralEscapedUtf16) {
   TypeParam ast_generator("RETURN '\\u221daaa\\u221daaa'");
   auto *query = ast_generator.query_;
   auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
-  auto *literal = dynamic_cast<PrimitiveLiteral *>(
-      return_clause->body_.named_expressions[0]->expression_);
-  ASSERT_TRUE(literal);
-  EXPECT_EQ(literal->value_.Value<std::string>(), u8"\u221daaa\u221daaa");
-  EXPECT_EQ(literal->token_position_, 2);
+  CheckLiteral(ast_generator.context_,
+               return_clause->body_.named_expressions[0]->expression_,
+               u8"\u221daaa\u221daaa", 2);
 }
 
 TYPED_TEST(CypherMainVisitorTest, StringLiteralEscapedUtf16Error) {
@@ -761,34 +686,25 @@ TYPED_TEST(CypherMainVisitorTest, StringLiteralEscapedUtf32) {
   TypeParam ast_generator("RETURN '\\U0001F600aaaa\\U0001F600aaaaaaaa'");
   auto *query = ast_generator.query_;
   auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
-  auto *literal = dynamic_cast<PrimitiveLiteral *>(
-      return_clause->body_.named_expressions[0]->expression_);
-  ASSERT_TRUE(literal);
-  EXPECT_EQ(literal->value_.Value<std::string>(),
-            u8"\U0001F600aaaa\U0001F600aaaaaaaa");
-  EXPECT_EQ(literal->token_position_, 2);
+  CheckLiteral(ast_generator.context_,
+               return_clause->body_.named_expressions[0]->expression_,
+               u8"\U0001F600aaaa\U0001F600aaaaaaaa", 2);
 }
 
 TYPED_TEST(CypherMainVisitorTest, DoubleLiteral) {
   TypeParam ast_generator("RETURN 3.5");
   auto *query = ast_generator.query_;
   auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
-  auto *literal = dynamic_cast<PrimitiveLiteral *>(
-      return_clause->body_.named_expressions[0]->expression_);
-  ASSERT_TRUE(literal);
-  EXPECT_EQ(literal->value_.Value<double>(), 3.5);
-  EXPECT_EQ(literal->token_position_, 2);
+  CheckLiteral(ast_generator.context_,
+               return_clause->body_.named_expressions[0]->expression_, 3.5, 2);
 }
 
 TYPED_TEST(CypherMainVisitorTest, DoubleLiteralExponent) {
   TypeParam ast_generator("RETURN 5e-1");
   auto *query = ast_generator.query_;
   auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
-  auto *literal = dynamic_cast<PrimitiveLiteral *>(
-      return_clause->body_.named_expressions[0]->expression_);
-  ASSERT_TRUE(literal);
-  EXPECT_EQ(literal->value_.Value<double>(), 0.5);
-  EXPECT_EQ(literal->token_position_, 2);
+  CheckLiteral(ast_generator.context_,
+               return_clause->body_.named_expressions[0]->expression_, 0.5, 2);
 }
 
 TYPED_TEST(CypherMainVisitorTest, ListLiteral) {
@@ -799,15 +715,11 @@ TYPED_TEST(CypherMainVisitorTest, ListLiteral) {
       return_clause->body_.named_expressions[0]->expression_);
   ASSERT_TRUE(list_literal);
   ASSERT_EQ(3, list_literal->elements_.size());
-  auto *elem_0 = dynamic_cast<PrimitiveLiteral *>(list_literal->elements_[0]);
-  ASSERT_TRUE(elem_0);
-  EXPECT_EQ(elem_0->value_.type(), TypedValue::Type::Int);
+  CheckLiteral(ast_generator.context_, list_literal->elements_[0], 3);
   auto *elem_1 = dynamic_cast<ListLiteral *>(list_literal->elements_[1]);
   ASSERT_TRUE(elem_1);
   EXPECT_EQ(0, elem_1->elements_.size());
-  auto *elem_2 = dynamic_cast<PrimitiveLiteral *>(list_literal->elements_[2]);
-  ASSERT_TRUE(elem_2);
-  EXPECT_EQ(elem_2->value_.type(), TypedValue::Type::String);
+  CheckLiteral(ast_generator.context_, list_literal->elements_[2], "johhny");
 }
 
 TYPED_TEST(CypherMainVisitorTest, MapLiteral) {
@@ -818,14 +730,10 @@ TYPED_TEST(CypherMainVisitorTest, MapLiteral) {
       return_clause->body_.named_expressions[0]->expression_);
   ASSERT_TRUE(map_literal);
   ASSERT_EQ(3, map_literal->elements_.size());
-  auto *elem_0 = dynamic_cast<PrimitiveLiteral *>(
-      map_literal->elements_[ast_generator.PropPair("a")]);
-  ASSERT_TRUE(elem_0);
-  EXPECT_EQ(elem_0->value_.type(), TypedValue::Type::Int);
-  auto *elem_1 = dynamic_cast<PrimitiveLiteral *>(
-      map_literal->elements_[ast_generator.PropPair("b")]);
-  ASSERT_TRUE(elem_1);
-  EXPECT_EQ(elem_1->value_.type(), TypedValue::Type::String);
+  CheckLiteral(ast_generator.context_,
+               map_literal->elements_[ast_generator.PropPair("a")], 1);
+  CheckLiteral(ast_generator.context_,
+               map_literal->elements_[ast_generator.PropPair("b")], "bla");
   auto *elem_2 = dynamic_cast<ListLiteral *>(
       map_literal->elements_[ast_generator.PropPair("c")]);
   ASSERT_TRUE(elem_2);
@@ -859,10 +767,9 @@ TYPED_TEST(CypherMainVisitorTest, NodePattern) {
                                  ast_generator.db_accessor_->Label("label3")));
   std::map<std::pair<std::string, GraphDbTypes::Property>, int64_t> properties;
   for (auto x : node->properties_) {
-    auto *literal = dynamic_cast<PrimitiveLiteral *>(x.second);
-    ASSERT_TRUE(literal);
-    ASSERT_TRUE(literal->value_.type() == TypedValue::Type::Int);
-    properties[x.first] = literal->value_.Value<int64_t>();
+    TypedValue value = LiteralValue(ast_generator.context_, x.second);
+    ASSERT_TRUE(value.type() == TypedValue::Type::Int);
+    properties[x.first] = value.Value<int64_t>();
   }
   EXPECT_THAT(properties,
               UnorderedElementsAre(Pair(ast_generator.PropPair("a"), 5),
@@ -952,10 +859,9 @@ TYPED_TEST(CypherMainVisitorTest, RelationshipPatternDetails) {
                            ast_generator.db_accessor_->EdgeType("type2")));
   std::map<std::pair<std::string, GraphDbTypes::Property>, int64_t> properties;
   for (auto x : edge->properties_) {
-    auto *literal = dynamic_cast<PrimitiveLiteral *>(x.second);
-    ASSERT_TRUE(literal);
-    ASSERT_TRUE(literal->value_.type() == TypedValue::Type::Int);
-    properties[x.first] = literal->value_.Value<int64_t>();
+    TypedValue value = LiteralValue(ast_generator.context_, x.second);
+    ASSERT_TRUE(value.type() == TypedValue::Type::Int);
+    properties[x.first] = value.Value<int64_t>();
   }
   EXPECT_THAT(properties,
               UnorderedElementsAre(Pair(ast_generator.PropPair("a"), 5),
@@ -1007,9 +913,7 @@ TYPED_TEST(CypherMainVisitorTest, RelationshipPatternLowerBounded) {
   AssertMatchSingleEdgeAtom(match, edge);
   EXPECT_EQ(edge->direction_, EdgeAtom::Direction::OUT);
   EXPECT_TRUE(edge->has_range_);
-  auto *lower_bound = dynamic_cast<PrimitiveLiteral *>(edge->lower_bound_);
-  ASSERT_TRUE(lower_bound);
-  EXPECT_TRUE(lower_bound->value_.Value<int64_t>() == 42);
+  CheckLiteral(ast_generator.context_, edge->lower_bound_, 42);
   EXPECT_EQ(edge->upper_bound_, nullptr);
 }
 
@@ -1022,9 +926,7 @@ TYPED_TEST(CypherMainVisitorTest, RelationshipPatternUpperBounded) {
   EXPECT_EQ(edge->direction_, EdgeAtom::Direction::OUT);
   EXPECT_TRUE(edge->has_range_);
   EXPECT_EQ(edge->lower_bound_, nullptr);
-  auto upper_bound = dynamic_cast<PrimitiveLiteral *>(edge->upper_bound_);
-  ASSERT_TRUE(upper_bound);
-  EXPECT_EQ(upper_bound->value_.Value<int64_t>(), 42);
+  CheckLiteral(ast_generator.context_, edge->upper_bound_, 42);
 }
 
 TYPED_TEST(CypherMainVisitorTest, RelationshipPatternLowerUpperBounded) {
@@ -1035,12 +937,8 @@ TYPED_TEST(CypherMainVisitorTest, RelationshipPatternLowerUpperBounded) {
   AssertMatchSingleEdgeAtom(match, edge);
   EXPECT_EQ(edge->direction_, EdgeAtom::Direction::OUT);
   EXPECT_TRUE(edge->has_range_);
-  auto lower_bound = dynamic_cast<PrimitiveLiteral *>(edge->lower_bound_);
-  ASSERT_TRUE(lower_bound);
-  EXPECT_EQ(lower_bound->value_.Value<int64_t>(), 24);
-  auto upper_bound = dynamic_cast<PrimitiveLiteral *>(edge->upper_bound_);
-  ASSERT_TRUE(upper_bound);
-  EXPECT_EQ(upper_bound->value_.Value<int64_t>(), 42);
+  CheckLiteral(ast_generator.context_, edge->lower_bound_, 24);
+  CheckLiteral(ast_generator.context_, edge->upper_bound_, 42);
 }
 
 TYPED_TEST(CypherMainVisitorTest, RelationshipPatternFixedRange) {
@@ -1051,12 +949,8 @@ TYPED_TEST(CypherMainVisitorTest, RelationshipPatternFixedRange) {
   AssertMatchSingleEdgeAtom(match, edge);
   EXPECT_EQ(edge->direction_, EdgeAtom::Direction::OUT);
   EXPECT_TRUE(edge->has_range_);
-  auto lower_bound = dynamic_cast<PrimitiveLiteral *>(edge->lower_bound_);
-  ASSERT_TRUE(lower_bound);
-  EXPECT_EQ(lower_bound->value_.Value<int64_t>(), 42);
-  auto upper_bound = dynamic_cast<PrimitiveLiteral *>(edge->upper_bound_);
-  ASSERT_TRUE(upper_bound);
-  EXPECT_EQ(upper_bound->value_.Value<int64_t>(), 42);
+  CheckLiteral(ast_generator.context_, edge->lower_bound_, 42);
+  CheckLiteral(ast_generator.context_, edge->upper_bound_, 42);
 }
 
 TYPED_TEST(CypherMainVisitorTest, RelationshipPatternFloatingUpperBound) {
@@ -1068,12 +962,8 @@ TYPED_TEST(CypherMainVisitorTest, RelationshipPatternFloatingUpperBound) {
   AssertMatchSingleEdgeAtom(match, edge);
   EXPECT_EQ(edge->direction_, EdgeAtom::Direction::OUT);
   EXPECT_TRUE(edge->has_range_);
-  auto lower_bound = dynamic_cast<PrimitiveLiteral *>(edge->lower_bound_);
-  ASSERT_TRUE(lower_bound);
-  EXPECT_EQ(lower_bound->value_.Value<int64_t>(), 1);
-  auto upper_bound = dynamic_cast<PrimitiveLiteral *>(edge->upper_bound_);
-  ASSERT_TRUE(upper_bound);
-  EXPECT_EQ(upper_bound->value_.Value<double>(), 0.2);
+  CheckLiteral(ast_generator.context_, edge->lower_bound_, 1);
+  CheckLiteral(ast_generator.context_, edge->upper_bound_, 0.2);
 }
 
 TYPED_TEST(CypherMainVisitorTest, RelationshipPatternUnboundedWithProperty) {
@@ -1086,9 +976,8 @@ TYPED_TEST(CypherMainVisitorTest, RelationshipPatternUnboundedWithProperty) {
   EXPECT_TRUE(edge->has_range_);
   EXPECT_EQ(edge->lower_bound_, nullptr);
   EXPECT_EQ(edge->upper_bound_, nullptr);
-  auto prop_literal = dynamic_cast<PrimitiveLiteral *>(
-      edge->properties_[ast_generator.PropPair("prop")]);
-  EXPECT_EQ(prop_literal->value_.Value<int64_t>(), 42);
+  CheckLiteral(ast_generator.context_,
+               edge->properties_[ast_generator.PropPair("prop")], 42);
 }
 
 TYPED_TEST(CypherMainVisitorTest,
@@ -1102,9 +991,8 @@ TYPED_TEST(CypherMainVisitorTest,
   EXPECT_TRUE(edge->has_range_);
   EXPECT_EQ(edge->lower_bound_, nullptr);
   EXPECT_EQ(edge->upper_bound_, nullptr);
-  auto prop_literal = dynamic_cast<PrimitiveLiteral *>(
-      edge->properties_[ast_generator.PropPair("prop")]);
-  EXPECT_EQ(prop_literal->value_.Value<int64_t>(), 42);
+  CheckLiteral(ast_generator.context_,
+               edge->properties_[ast_generator.PropPair("prop")], 42);
   ASSERT_EQ(edge->edge_types_.size(), 1U);
   auto edge_type = ast_generator.db_accessor_->EdgeType("edge_type");
   EXPECT_EQ(edge->edge_types_[0], edge_type);
@@ -1119,12 +1007,9 @@ TYPED_TEST(CypherMainVisitorTest, RelationshipPatternUpperBoundedWithProperty) {
   EXPECT_EQ(edge->direction_, EdgeAtom::Direction::OUT);
   EXPECT_TRUE(edge->has_range_);
   EXPECT_EQ(edge->lower_bound_, nullptr);
-  auto upper_bound = dynamic_cast<PrimitiveLiteral *>(edge->upper_bound_);
-  ASSERT_TRUE(upper_bound);
-  EXPECT_EQ(upper_bound->value_.Value<int64_t>(), 2);
-  auto prop_literal = dynamic_cast<PrimitiveLiteral *>(
-      edge->properties_[ast_generator.PropPair("prop")]);
-  EXPECT_EQ(prop_literal->value_.Value<int64_t>(), 42);
+  CheckLiteral(ast_generator.context_, edge->upper_bound_, 2);
+  CheckLiteral(ast_generator.context_,
+               edge->properties_[ast_generator.PropPair("prop")], 42);
 }
 
 // // PatternPart with variable.
@@ -1510,9 +1395,7 @@ TYPED_TEST(CypherMainVisitorTest, MatchBfsReturn) {
   EXPECT_EQ(bfs->identifier_->name_, "r");
   EXPECT_EQ(bfs->traversed_edge_identifier_->name_, "e");
   EXPECT_EQ(bfs->next_node_identifier_->name_, "n");
-  auto *max_depth = dynamic_cast<PrimitiveLiteral *>(bfs->max_depth_);
-  ASSERT_TRUE(max_depth);
-  EXPECT_EQ(max_depth->value_.Value<int64_t>(), 10U);
+  CheckLiteral(ast_generator.context_, bfs->max_depth_, 10);
   auto *eq = dynamic_cast<EqualOperator *>(bfs->filter_expression_);
   ASSERT_TRUE(eq);
 }