#include <gqlite.h>
#include <gqlite-c.h>
//BEGIN format.h


#include <gqlite.h>

#include <string>

namespace gqlite
{
  template<typename T> concept std_to_stringable = requires (T a) {
      std::to_string(a);
  };
  template<typename T> concept container_begin_end = requires (T a) {
      a.begin(); a.end();
  };

  /**
   * @internal
   * @return \p _integer converted to a string, with a minimum size of \p _width, filled with 0.
   */
  inline std::string to_string_fixed_width(int _integer, std::size_t _width)
  {
    std::string s = std::to_string(_integer);
    while(s.size() < _width)
    {
      s = '0' + s;
    }
    return s;
  }
  template<typename _T_>
  struct to_string_impl;
  template<typename _T_>
  inline std::string to_string(const _T_& _v)
  {
    return to_string_impl<_T_>::to_string(_v);
  }
  template<typename _T_>  requires std_to_stringable<_T_>
  struct to_string_impl<_T_>
  {
    static inline std::string to_string(const _T_& _v)
    {
      return std::to_string(_v);
    }
  };

  template<typename _T_>  requires container_begin_end<_T_>
  struct to_string_impl<_T_>
  {
    static inline std::string to_string(const _T_& _c)
    {
      std::string r = "(";
      for(auto v: _c)
      {
        r += gqlite::to_string(v) + " ";
      }
      r += ")";
      return r;
    }
  };

  const char* to_string(gqlite::value_type _type);
  std::string to_string(const value& _value);
  template<>
  inline std::string to_string<std::string>(const std::string& _v)
  {
    return _v;
  }
  template<>
  inline std::string to_string<const char*>(const char* const& _v)
  {
    return _v;
  }
  template<>
  inline std::string to_string<char>(const char& _v)
  {
    std::string v;
    return v += _v;
  }
  namespace details
  {
    inline std::string format_string(std::size_t, const std::string& _format)
    {
      return _format;
    }
    template<typename _T_, typename... _Targs_>
    inline std::string format_string(std::size_t _start_pos, const std::string& _format, const _T_& _a, const _Targs_&... _args)
    {
      std::size_t start_pos = _format.find("{}", _start_pos);
      if(start_pos == std::string::npos)
      {
        return _format;
      }
      std::string f = _format;
      std::string replacement = to_string(_a);
      f.replace(start_pos, 2, replacement);
      return format_string(start_pos + replacement.size(), f, _args...);
    }
  }
  template<typename... _Targs_>
  inline std::string format_string(const std::string& _format, const _Targs_&... _args)
  {
    return details::format_string(0, _format, _args...);
  }
  namespace details
  {
    template<typename... _Targs_>
    struct formatter
    {
      template<typename _Targ_>
      formatter<_Targs_..., _Targ_> operator ,(const _Targ_& _arg)
      {
        return formatter<_Targs_..., _Targ_>{std::tuple_cat(m_args, std::make_tuple(_arg))};
      }
      operator std::string() const
      {
        return std::apply(&gqlite::format_string<_Targs_...>, std::tuple_cat(m_args));
      }
      std::tuple<std::string, _Targs_...> m_args;
    };
  }
  template<typename _Targ_>
  inline details::formatter<_Targ_> operator%(const std::string& _format, const _Targ_& _arg)
  {
    return details::formatter<_Targ_>{std::make_tuple(_format, _arg)};
  }
}
//END format.h
//BEGIN logging.h
/**
 * logging file allowing optional support of clog
 */



#include <iostream>


#define gqlite_error(msg, ...) std::cerr << "ERROR: " __FILE__ ":" << __LINE__ << ": " << gqlite::format_string(msg __VA_OPT__(,) __VA_ARGS__) << std::endl;
#define gqlite_warning(msg, ...) std::cerr << "WARNING: " __FILE__ ":" << __LINE__ << ": " << gqlite::format_string(msg __VA_OPT__(,) __VA_ARGS__) << std::endl;
#define gqlite_debug(msg, ...) std::cerr << "DEBUG: " __FILE__ ":" << __LINE__ << ": " << gqlite::format_string(msg __VA_OPT__(,) __VA_ARGS__) << std::endl;
#define gqlite_info(msg, ...) std::cerr << "INFO: " __FILE__ ":" << __LINE__ << ": " << gqlite::format_string(msg __VA_OPT__(,) __VA_ARGS__) << std::endl;
#define gqlite_fatal(msg, ...) std::cerr << "FATAL ERROR" __FILE__ ":" << __LINE__ << ": " << gqlite::format_string(msg __VA_OPT__(,) __VA_ARGS__) << std::endl; std::abort()

#if NDEBUG

#define gqlite_assert(assrt) do { } while ((false) && (assrt))
#define gqlite_assert_msg(assrt, msg, ...) gqlite_assert(assrt)
#define gqlite_near_equal_assert(v1, v2, tol) (void)(v1); (void)(v2); (void)(tol); do { } while (false)

#else

#define gqlite_assert(assrt)                                     \
  if( not (assrt ) )                                                \
  {                                                                 \
    std::cerr << "assertion failed: " << #assrt << std::endl;       \
    std::abort();                                                   \
  }

#define gqlite_assert_msg(assrt, msg, ...)                                    \
  if( not (assrt ) )                                                        \
  {                                                                         \
    std::cerr << "assertion failed: " << msg << " " << #assrt << std::endl; \
    std::abort();                                                           \
  }

#define gqlite_near_equal_assert(v1, v2, tol)                                                          \
  {                                                                                                       \
    auto __clog__nea_ev1_ = (v1);                                                                         \
    auto __clog__nea_ev2_ = (v2);                                                                         \
    auto __clog__nea_tol_ = (tol);                                                                        \
    auto __clog__nea_error_ = std::abs(__clog__nea_ev1_ - __clog__nea_ev2_);                              \
    if(__clog__nea_error_ > __clog__nea_tol_)                                                             \
    {                                                                                                     \
      std::cerr << "Expected near equal: '" << __clog__nea_ev1_ << "' != '" << __clog__nea_ev2_           \
                << "' error: '" << __clog__nea_error_ << "', tolerance: '" << __clog__nea_tol_ << "'");   \
    }                                                                                                     \
  }


#endif
//END logging.h
//BEGIN string.h
#include <numeric>
#include <sstream>

namespace gqlite::string
{
  /**
   * @internal
   * @return the result of joining the element of a vector with delimiter
   */
  template<class _C_>
  inline std::string join(const _C_& _v, const std::string& _delimiter)
  {
    if(_v.empty()) return std::string();
    return std::accumulate(
      std::next(_v.begin()), 
      _v.end(), 
      *_v.begin(), 
      [_delimiter](std::string a, std::string b) {
          return a + _delimiter + b;
      }
    );
  }
  /**
   * @internal
   * @return the result of spliting \p s with delimiter \p delim
   */
  inline std::vector<std::string> split (const std::string &s, char delim)
  {
    std::vector<std::string> result;
    std::stringstream ss (s);
    std::string item;

    while (std::getline (ss, item, delim))
    {
      if(not item.empty()) result.push_back (item);
    }

    return result;
  }
}
//END string.h
//BEGIN gqlite_p.h


#include <gqlite.h>



namespace gqlite
{
  enum class exception_stage
  {
    unspecified,
    compiletime,
    runtime
  };
  enum class exception_code
  {
    unspecified,
    // Generic error code
    internal_error, ///< this should never happen
    invalid_json, ///< error that occurs when parsing json
    table_error,
    value_error, ///< generic error that occurs in the value class
    unimplemented_error,
    // OpenCypher error code
    column_name_conflict = 100,
    integer_overflow,
    invalid_argument_type,
    invalid_delete,
    invalid_number_literal,
    invalid_number_of_arguments,
    invalid_parameter_use,
    negative_integer_argument,
    non_constant_expression,
    parse_error,
    requires_directed_relationship,
    undefined_variable,
    unexpected_syntax,
    unknown_function,
    variable_already_bound,
    variable_type_conflict
  };

  exception_code get_execption_code(const exception& _exception);
  std::string get_execption_message(const exception& _exception);
  [[noreturn]] void throw_exception(exception_stage _stage, exception_code _code, const std::string& _error);
  [[noreturn]] void throw_exception(exception_stage _stage, exception_code _code, const char* _error);
  [[noreturn]] void rethrow_exception(exception_stage _stage, const exception& _ex);
  template<typename _T_, typename... _TOther_>
  [[noreturn]] inline void throw_exception(exception_stage _stage, exception_code _code, const char* _format, const _T_& _value, const _TOther_&... _other)
  {
    throw_exception(_stage, _code, format_string(_format, _value, _other...));
  }
  template<typename _T_, typename... _TOther_>
  [[noreturn]] inline void throw_exception(exception_stage _stage, exception_code _code, const std::string& _format, const _T_& _value, const _TOther_&... _other)
  {
    throw_exception(_stage, _code, format_string(_format, _value, _other...));
  }
  /**
   * Represent the value type.
   */
  enum class value_type
  {
    invalid,
    boolean,
    integer,
    floating_point,
    string,
    map,
    vector
  };

  template<typename _T_>
  inline value::value(const std::initializer_list<typename std::unordered_map<std::string, _T_>::value_type>& _v) : value()
  {
    std::unordered_map<std::string, _T_> m{_v};
    std::unordered_map<std::string, _T_> mo;
    for(auto const&[k, v] : m)
    {
      mo[k] = v;
    }
    *this = value(mo);
  }
  template<typename _T_>
  inline value::value(const std::vector<_T_>& _v) : value()
  {
    value_vector vo;
    for(const _T_& t : _v)
    {
      vo.push_back(t);
    }
    *this = value(vo);
  }
  inline value remove_invalid_in_map(const value& _value)
  {
    switch(_value.get_type())
    {
      using enum value_type;
      case invalid:
      case boolean:
      case integer:
      case floating_point:
      case string:
        return _value;
      case map:
      {
        value_map vm;
        for(const auto& [k, v] : _value.to_map())
        {
          if(v.get_type() != invalid)
          {
            vm[k] = remove_invalid_in_map(v);
          }
        }
        return vm;
      }
      case vector:
      {
        value_vector vv;
        for(const value& v : _value.to_vector())
        {
          if(v.get_type() != invalid)
          {
            vv.push_back(remove_invalid_in_map(v));
          }
        }
        return vv;
      }
    }
    throw_exception(exception_stage::unspecified, exception_code::internal_error, "Unknown value type for {}.", _value);
  }
}
//END gqlite_p.h
//BEGIN global.h


#include <ranges>

#define GQLITE_UNUSED (void)

//BEGIN gqlite Foreach

// The following macros are for internal use by gqlite, no warranty of behavior is guaranteed

#define __GQLITE_INC(I) __GQLITE_INC_## I
#define __GQLITE_INC_0 1
#define __GQLITE_INC_1 2
#define __GQLITE_INC_2 3
#define __GQLITE_INC_3 4
#define __GQLITE_INC_4 5
#define __GQLITE_INC_5 6
#define __GQLITE_INC_6 7
#define __GQLITE_INC_7 8
#define __GQLITE_INC_8 9
#define __GQLITE_INC_9 10
#define __GQLITE_INC_10 11
#define __GQLITE_INC_11 12
#define __GQLITE_INC_12 13
#define __GQLITE_INC_13 14
#define __GQLITE_INC_14 15
#define __GQLITE_INC_15 16
#define __GQLITE_INC_16 17
#define __GQLITE_INC_17 18
#define __GQLITE_INC_18 19
#define __GQLITE_INC_19 20
#define __GQLITE_INC_20 21

#define __GQLITE_FOREACH__0(WHAT, SEP, I, ...)
#define __GQLITE_FOREACH__1(WHAT, SEP, I, X, ...) WHAT(X, I)
#define __GQLITE_FOREACH__2(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__1(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__3(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__2(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__4(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__3(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__5(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__4(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__6(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__5(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__7(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__6(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__8(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__7(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__9(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__8(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__10(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__9(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__11(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__10(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__12(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__11(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__13(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__12(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__14(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__13(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__15(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__14(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__16(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__15(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__17(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__16(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__18(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__17(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__19(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__18(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__20(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__19(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__21(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__20(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__22(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__21(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__23(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__22(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__24(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__23(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__25(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__24(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__26(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__25(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__27(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__26(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__28(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__27(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__29(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__28(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__30(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__29(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)

#define __GQLITE_GET_MACRO(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, NAME, ...) NAME

/**
 * Execute action(X, I) on each variadic arguments.
 * X will contains the argument and I the index.
 * And add sep between each element
 * 
 * @code
 * #define PRINT(X, I) std::cout << " " # I ": " << X;
 * GQLITE_FOREACH(PRINT, do();, a, b, c)
 * @endcode
 * 
 * Will expand into:
 * 
 * @code
 * std::cout << " " "0" ": " << a; do();
 * std::cout << " " "1" ": " << b; do();
 * std::cout << " " "2" ": " << c;
 * @endcode
 */
#define GQLITE_FOREACH_SEP(action, sep, ...) \
  __GQLITE_GET_MACRO(void, __VA_ARGS__ __VA_OPT__(,) \
  __GQLITE_FOREACH__29, __GQLITE_FOREACH__28, __GQLITE_FOREACH__27, __GQLITE_FOREACH__26, __GQLITE_FOREACH__25, __GQLITE_FOREACH__24, __GQLITE_FOREACH__23, __GQLITE_FOREACH__22, __GQLITE_FOREACH__21, __GQLITE_FOREACH__20, \
  __GQLITE_FOREACH__19, __GQLITE_FOREACH__18, __GQLITE_FOREACH__17, __GQLITE_FOREACH__16, __GQLITE_FOREACH__15, __GQLITE_FOREACH__14, __GQLITE_FOREACH__13, __GQLITE_FOREACH__12, __GQLITE_FOREACH__11, __GQLITE_FOREACH__10, \
  __GQLITE_FOREACH__9, __GQLITE_FOREACH__8, __GQLITE_FOREACH__7, __GQLITE_FOREACH__6, __GQLITE_FOREACH__5, __GQLITE_FOREACH__4, __GQLITE_FOREACH__3, __GQLITE_FOREACH__2, __GQLITE_FOREACH__1, __GQLITE_FOREACH__0)(action, (sep), 0, __VA_ARGS__)

/**
 * Execute action(X, I) on each variadic arguments.
 * X will contains the argument and I the index.
 * 
 * @code
 * #define PRINT(X, I) std::cout << " " # I ": " << X;
 * GQLITE_FOREACH(PRINT, a, b, c)
 * @endcode
 * 
 * Will expand into:
 * 
 * @code
 * std::cout << " " "0" ": " << a;
 * std::cout << " " "1" ": " << b;
 * std::cout << " " "2" ": " << c;
 * @endcode
 */
#define GQLITE_FOREACH(action, ...) GQLITE_FOREACH_SEP(action, , __VA_ARGS__)

//END gqlite Foreach

//BEGIN gqlite List

#define __GQLITE_LIST_ACTION(__X__, __I__) , __X__

/**
 * Concatenate the arguments of the macro in a comma seperated list.
 * Usefull to enable multiple template parameters in a macro.
 * 
 * @code
 * SOME_MACRO(some_template<GQLITE_LIST(A,B)>)
 * @endcode
 */
#define GQLITE_LIST(__A__, ...) __A__ GQLITE_FOREACH(__GQLITE_LIST_ACTION, __VA_ARGS__)

//END gqlite List

namespace gqlite::traits
{
  /**
   * Define a trait that test if \ref T is in the set \ref Ts.
   * 
   * \code
   * contains_v<int, std::string, double> == false;
   * contains_v<std::string, std::string, double> == true;
   * \endcode
   */
  template<typename T, typename... Ts>
  struct contains : std::disjunction<std::is_same<T, Ts>...>
  {};
  template<typename T, typename... Ts>
  inline constexpr bool contains_v = contains<T, Ts...>::value;
}

namespace gqlite::workarounds::views
{
  template<typename _T_>
  inline auto values(const _T_& _nodes)
  { //work around a bug in gcc 11 where std::views::values works on a const& but not on the value directly 
    return std::views::values(_nodes);
  }
  template<typename _T_>
  inline auto keys(const _T_& _nodes)
  { //work around a bug in gcc 11 where std::views::values works on a const& but not on the value directly 
    return std::views::keys(_nodes);
  }
}
//END global.h
//BEGIN oc/algebra/expression_info.h


namespace gqlite::oc::algebra
{
  enum class expression_type
  {
    empty, value, boolean, integer, floating_point, string, map, vector, node, edge, path
  };
  struct expression_info
  {
    expression_type type = expression_type::empty;
    bool constant = true;
  };
}
//END oc/algebra/expression_info.h
//BEGIN errors.h
#include <algorithm>





namespace gqlite::errors
{
  template<typename... _TOther_>
  inline void check_condition(bool _condition, exception_stage _stage, exception_code _code, const char* _format, const _TOther_&... _other)
  {
    if(not _condition)
    {
      throw_exception(_stage, _code, _format, _other...);
    }
  }
  template<typename... _TOther_>
  inline void check_condition(bool _condition, exception_stage _stage, exception_code _code, const std::string& _format, const _TOther_&... _other)
  {
    if(not _condition)
    {
      throw_exception(_stage, _code, _format, _other...);
    }
  }
  template<typename _T_>
  inline void check_arguments_size(const char* _fname, const std::vector<_T_>& _arguments, std::size_t _size)
  {
    check_condition(_arguments.size() == _size, exception_stage::runtime, exception_code::invalid_number_of_arguments, "'{}' function expect {} arguments, got {}", _fname, _size, _arguments.size());
  }
  template<typename _T_, typename... _TOther_>
  inline void check_argument_type(_T_ _got, _T_ _expected, exception_stage _stage, const char* _format, const _TOther_&... _other)
  {
    check_condition(_got == _expected, _stage, exception_code::invalid_argument_type, _format, _other...);
  }
  template<typename _T_, typename... _TOther_>
  inline void check_argument_types(_T_ _got, std::initializer_list<_T_> _expected, exception_stage _stage, const char* _format, const _TOther_&... _other)
  {
    check_condition(std::find(_expected.begin(), _expected.end(), _got) != _expected.end(), _stage, exception_code::invalid_argument_type, _format, _other...);
  }

  template<typename _T_, typename... _TOther_>
  inline void check_argument_type_function(_T_ _got, _T_ _expected, const char* _fname, const _TOther_&... _other)
  {
    check_argument_type(_got, _expected, exception_stage::runtime, "Invalid argument type for function '{}'.", _fname, _other...);
  }
  inline void check_argument_is_map(const char* _fname, const value& _value)
  {
    check_argument_type_function(_value.get_type(), value_type::map, "Function '{}' expect a map, got '{}'.", _fname, _value);
  }
  template<typename... _TOther_>
  [[noreturn]] inline void invalid_argument_type(exception_stage _stage, const char* _format, const _TOther_&... _other)
  {
    throw_exception(_stage, exception_code::invalid_argument_type, _format, _other...);
  }
  [[noreturn]] inline void undefined_variable(exception_stage _stage, const std::string& _variable_name)
  {
    throw_exception(_stage, exception_code::undefined_variable, "Unknown variable '{}'.", _variable_name);
  }
  inline void check_variable_type(oc::algebra::expression_type _actual_type, oc::algebra::expression_type _expected_type, exception_stage _stage, const std::string& _varname)
  {
    check_condition(_actual_type == _expected_type, _stage, exception_code::variable_type_conflict, "{} is redefined as a variable of a different type.", _varname);
  }
  template<typename... _TOther_>
  inline void check_constant(bool _constant, exception_stage _stage, const char* _format, const _TOther_&... _other)
  {
    check_condition(_constant, _stage, exception_code::non_constant_expression, _format, _other...);
  }
}
//END errors.h
//BEGIN oc/token.h
#include <string>

namespace gqlite::oc
{
  enum class token_type
  {
  // Not really token
    UNFINISHED_STRING = -4,
    UNFINISHED_COMMENT = -3,
    END_OF_FILE = -2,
    UNKNOWN = - 1,
  // Special characters
    SEMI = 0, ///< ;
    STARTBRACE, ///< {
    ENDBRACE, ///< }
    STARTBRACKET, ///< (
    ENDBRACKET, ///< )
    STARTBOXBRACKET, ///< [
    ENDBOXBRACKET, ///< ]
    PIPE, ///< |
    MINUS, ///< -
    COMMA, ///< ,
    COLON, ///< :
    COLONCOLON, ///< :
    EQUAL, ///< =
    DIFFERENT, ///< !=
    SUPERIOR, ///< >
    INFERIOR, ///< <
    SUPERIOR_EQUAL, ///< >=
    INFERIOR_EQUAL, ///< <=
    DOT, ///< .
    DOTDOT, ///< ..
    LEFT_ARROW, ///< ->
    RIGHT_ARROW, ///< <-
    PLUS, ///< +
    STAR, ///< *
    DIVIDE, ///< /
    EXCLAMATION, ///< !
    PLUS_EQUAL, ///< +=
    PERCENT, ///< %
  // Constants
    PARAMETER,
    IDENTIFIER,
    STRING,
    INTEGER,
    FLOATING_POINT,
  // Keywords
#define TOKEN_KEYWORD(_K_) _K_,
#define TOKEN_KEYWORD2(_K_, _S_) _K_,
    TOKEN_KEYWORD(AND)
TOKEN_KEYWORD(AS)
TOKEN_KEYWORD(ASC)
TOKEN_KEYWORD(BY)
TOKEN_KEYWORD(CALL)
TOKEN_KEYWORD(CREATE)
TOKEN_KEYWORD(DELETE)
TOKEN_KEYWORD(DESC)
TOKEN_KEYWORD(DETACH)
TOKEN_KEYWORD(FALSE)
TOKEN_KEYWORD(IN)
TOKEN_KEYWORD(IS)
TOKEN_KEYWORD(LIMIT)
TOKEN_KEYWORD(NOT)
TOKEN_KEYWORD(MATCH)
TOKEN_KEYWORD2(NULL_CAPS, "NULL")
TOKEN_KEYWORD2(NULL_TOKEN, "null")
TOKEN_KEYWORD(OR)
TOKEN_KEYWORD(ORDER)
TOKEN_KEYWORD(REMOVE)
TOKEN_KEYWORD(RETURN)
TOKEN_KEYWORD(SET)
TOKEN_KEYWORD(SKIP)
TOKEN_KEYWORD(TRUE)
TOKEN_KEYWORD(UNWIND)
TOKEN_KEYWORD(XOR)
TOKEN_KEYWORD(YIELD)
TOKEN_KEYWORD(WITH)
TOKEN_KEYWORD(WHERE)

#undef TOKEN_KEYWORD
#undef TOKEN_KEYWORD2
  };
  const char* token_type_to_string(token_type _type );
  struct token
  {
    /// type of the token
    token_type type;
    /// line of the token
    int line;
    /// Column of the token
    int column;
    /// String or identifier name
    std::string string;
    
    token();
    /**
      * Creates a token of the given type
      */
    token(token_type _type, int _line, int _column);
    /**
      * Creates an identifier or a string constant
      */
    token(token_type _type, const std::string& _string, int _line, int _column);
  };
} // namespace gqlite::oc
//END oc/token.h
//BEGIN oc/lexer.h
#include <istream>

namespace gqlite::oc
{

class token;
class lexer
{
  public:
    struct mode
    {
      static constexpr int normal = 0x0;
      static constexpr int disable_keywords = 0x1;
    };
  public:
    lexer(std::istream* sstream);
    ~lexer();
  public:
    token next_token(int _flags = mode::normal);
    void unget(const token& _token);
  protected:
    bool ignore_comment(token& _token, int _lastChar);
    /**
     * @return the next char and increment the column counter.
     */
    int get_next_char();
    /**
     * @return the next char that is not a separator (space, tab, return...)
     */
    int get_next_non_separator_char();
    /**
     * Cancel the reading of the previous char.
     */
    void unget();
    bool eof() const;
    int line() const;
    int column() const;
    /**
     * Get an identifier (or keyword) in the current flow of character.
     */
    std::string get_identifier(int lastChar);
    token get_string(int lastChar);
  private:
    struct data;
    data* const d;
};

} // namespace gqlite::oc
//END oc/lexer.h
//BEGIN oc/algebra/node_type.h
namespace gqlite::oc::algebra
{
  enum class node_type
  {
#define OC_ALGEBRA_GENERATE(_KLASS_NAME_, _MEMBER_DEF_)                                                                                             \
  _KLASS_NAME_,
// Queries

#define OC_ALGEBRA_CREATE_NODES_MEMBERS(_KLASS_NAME_, F)                                    \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)

OC_ALGEBRA_GENERATE(create, OC_ALGEBRA_CREATE_NODES_MEMBERS)

#define OC_ALGEBRA_MATCH_NODES_MEMBERS(_KLASS_NAME_, F)                                     \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)  \
  F(_KLASS_NAME_, node_csp, where)

OC_ALGEBRA_GENERATE(match, OC_ALGEBRA_MATCH_NODES_MEMBERS)

// Statements

#define OC_ALGEBRA_NODES_LIST_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, nodes)

OC_ALGEBRA_GENERATE(statements, OC_ALGEBRA_NODES_LIST_MEMBERS)

#define OC_ALGEBRA_RETURN_MEMBERS(_KLASS_NAME_, F)                \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions) \
  F(_KLASS_NAME_, modifiers_csp, modifiers)

OC_ALGEBRA_GENERATE(return_statement, OC_ALGEBRA_RETURN_MEMBERS)

#define OC_ALGEBRA_WITH_MEMBERS(_KLASS_NAME_, F)                  \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions) \
  F(_KLASS_NAME_, modifiers_csp, modifiers)

OC_ALGEBRA_GENERATE(with, OC_ALGEBRA_WITH_MEMBERS)

#define OC_ALGEBRA_UNWIND_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, std::string, name)                \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(unwind, OC_ALGEBRA_UNWIND_MEMBERS)

#define OC_ALGEBRA_DELETE_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, bool, detach)                     \
  F(_KLASS_NAME_, std::vector<node_csp>, expressions)

OC_ALGEBRA_GENERATE(delete_statement, OC_ALGEBRA_DELETE_MEMBERS)

OC_ALGEBRA_GENERATE(set, OC_ALGEBRA_NODES_LIST_MEMBERS)
OC_ALGEBRA_GENERATE(remove, OC_ALGEBRA_NODES_LIST_MEMBERS)

#define OC_ALGEBRA_CALL_MEMBERS(_KLASS_NAME_, F)    \
  F(_KLASS_NAME_, std::string, name)                \
  F(_KLASS_NAME_, std::vector<node_csp>, arguments) \
  F(_KLASS_NAME_, std::vector<std::string>, yield)

OC_ALGEBRA_GENERATE(call, OC_ALGEBRA_CALL_MEMBERS)


#define OC_ALGEBRA_ORDER_BY_MEMBERS(_KLASS_NAME_, F)                  \
  F(_KLASS_NAME_, std::vector<order_by_expression_csp>, expressions)

OC_ALGEBRA_GENERATE(order_by, OC_ALGEBRA_ORDER_BY_MEMBERS)

#define OC_ALGEBRA_MODIFIERS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, skip)                     \
  F(_KLASS_NAME_, node_csp, limit)                    \
  F(_KLASS_NAME_, order_by_csp, order_by)

OC_ALGEBRA_GENERATE(modifiers, OC_ALGEBRA_MODIFIERS_MEMBERS)

// Order By Expression

#define OC_ALGEBRA_ORDER_BY_EXPRESSION_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, bool, asc)                          \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(order_by_expression, OC_ALGEBRA_ORDER_BY_EXPRESSION_MEMBERS)

// Set/remove Expressions

#define OC_ALGEBRA_SET_ADD_VALUE_MEMBERS(_KLASS_NAME_, F)       \
  F(_KLASS_NAME_, std::string, left)                            \
  F(_KLASS_NAME_, std::vector<std::string>, path)               \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(set_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)


#define OC_ALGEBRA_SET_EQUAL_MEMBERS(_KLASS_NAME_, F)                     \
  F(_KLASS_NAME_, std::string, left)                            \
  F(_KLASS_NAME_, std::vector<std::string>, path)               \
  F(_KLASS_NAME_, nocde_csp, expression)

OC_ALGEBRA_GENERATE(add_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)

#define OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS(_KLASS_NAME_, F)     \
  F(_KLASS_NAME_, std::string, left)                            \
  F(_KLASS_NAME_, std::vector<std::string>, path)

OC_ALGEBRA_GENERATE(remove_property, OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS)

#define OC_ALGEBRA_EDIT_LABELS_MEMBERS(_KLASS_NAME_, F)                   \
  F(_KLASS_NAME_, std::string, target)                                    \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(edit_labels, OC_ALGEBRA_EDIT_LABELS_MEMBERS)


// Values

#define OC_ALGEBRA_NO_MEMBERS(_KLASS_NAME_, F)

OC_ALGEBRA_GENERATE(end_of_list, OC_ALGEBRA_NO_MEMBERS)

#define OC_ALGEBRA_GRAPH_NODE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, node_csp, properties)

OC_ALGEBRA_GENERATE(graph_node, OC_ALGEBRA_GRAPH_NODE_MEMBERS)

#define OC_ALGEBRA_GRAPH_EDGE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, graph_node_csp, source)                       \
  F(_KLASS_NAME_, graph_node_csp, destination)                  \
  F(_KLASS_NAME_, edge_directivity, directivity)                \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, node_csp, properties)

OC_ALGEBRA_GENERATE(graph_edge, OC_ALGEBRA_GRAPH_EDGE_MEMBERS)

#define OC_ALGEBRA_VALUE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, gqlite::value, value)

OC_ALGEBRA_GENERATE(value, OC_ALGEBRA_VALUE_MEMBERS)


#define OC_ALGEBRA_MAP_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::unordered_map<GQLITE_LIST(std::string, node_csp)>, map)

OC_ALGEBRA_GENERATE(map, OC_ALGEBRA_MAP_MEMBERS)

#define OC_ALGEBRA_ARRAY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, array)

OC_ALGEBRA_GENERATE(array, OC_ALGEBRA_ARRAY_MEMBERS)


// Expressions

#define OC_ALGEBRA_NAMED_EXPRESSION(_KLASS_NAME_, F)      \
  F(_KLASS_NAME_, std::string, name)                      \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(named_expression, OC_ALGEBRA_NAMED_EXPRESSION)

#define OC_ALGEBRA_VARIABLE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, identifier)

OC_ALGEBRA_GENERATE(variable, OC_ALGEBRA_VARIABLE_MEMBERS)

#define OC_ALGEBRA_MEMBER_ACCESS_MEMBERS(_KLASS_NAME_, F)   \
  F(_KLASS_NAME_, node_csp, left)                           \
  F(_KLASS_NAME_, std::vector<std::string>, path)

OC_ALGEBRA_GENERATE(member_access, OC_ALGEBRA_MEMBER_ACCESS_MEMBERS)

#define OC_ALGEBRA_INDEXED_ACCESS_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, node_csp, left)                           \
  F(_KLASS_NAME_, node_csp, index)                          \
  F(_KLASS_NAME_, node_csp, end)

OC_ALGEBRA_GENERATE(indexed_access, OC_ALGEBRA_INDEXED_ACCESS_MEMBERS)

#define OC_ALGEBRA_HAS_LABELS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, left)                   \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(has_labels, OC_ALGEBRA_HAS_LABELS_MEMBERS)

#define OC_ALGEBRA_FUNCTION_CALL_EXPRESSION(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, std::string, name)                          \
  F(_KLASS_NAME_, std::vector<node_csp>, arguments)

OC_ALGEBRA_GENERATE(function_call, OC_ALGEBRA_FUNCTION_CALL_EXPRESSION)

#define OC_ALGEBRA_BINARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, left)                  \
  F(_KLASS_NAME_, node_csp, right)

OC_ALGEBRA_GENERATE(logical_and, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(logical_or, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(logical_xor, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_different, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_in, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_not_in, OC_ALGEBRA_BINARY_MEMBERS)

OC_ALGEBRA_GENERATE(addition, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(substraction, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(multiplication, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(division, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(modulo, OC_ALGEBRA_BINARY_MEMBERS)

#define OC_ALGEBRA_UNARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, value)

OC_ALGEBRA_GENERATE(logical_negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_null, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_not_null, OC_ALGEBRA_UNARY_MEMBERS)

#undef OC_ALGEBRA_GENERATE
    __Invalid__
  };
  const char* node_type_name(node_type _nt);
}
//END oc/algebra/node_type.h
//BEGIN oc/algebra/node.h
#ifndef _OC_ALGEBRA_ABSTRACT_NODE_H_
#define _OC_ALGEBRA_ABSTRACT_NODE_H_

#include <functional>
#include <memory>





namespace gqlite::oc::algebra
{
  enum class edge_directivity
  {
    directed, undirected
  };
  template<typename _TR_, typename... _TArgs_>
  class abstract_node_visitor;
  namespace details
  {
    class abstract_node_visitor_adaptor;
  }
  
  class node : public std::enable_shared_from_this<node>
  {
    template<typename _TR_, typename... _TArgs_>
    friend class abstract_node_visitor;
  protected:
    struct data;
    data* d;
    node(node_type _type, data* );
  public:
    virtual ~node();
  public:
    node_type get_type() const;
    virtual bool equals(const std::shared_ptr<const node>& _node) const = 0;
  protected:
    virtual void accept(details::abstract_node_visitor_adaptor* _node, void* _r, void* _parameter) const = 0;
  };
  typedef std::shared_ptr<const node> node_csp;
  template<typename... _T_>
  class alternative
  {
    static_assert(std::conjunction_v<std::is_base_of<node, _T_>...> , "All types should subclass node");
  public:
    alternative() : m_type(node_type::__Invalid__)
    {
    }
    template<typename _To_, std::enable_if_t<traits::contains_v<_To_, _T_...>, int> = 0>
    alternative(std::shared_ptr<const _To_> _v) : m_node(_v), m_type(_v->get_type())
    {}
    template<typename _To_, std::enable_if_t<traits::contains_v<_To_, _T_...>, int> = 0>
    alternative(const _To_* _v) : m_node(_v), m_type(_v->get_type())
    {}
    node_type get_type() const { return m_type; }
    template<typename _To_, std::enable_if_t<traits::contains_v<_To_, _T_...>, int> = 0>
    std::shared_ptr<const _To_> get_value() const
    {
      return std::static_pointer_cast<const _To_>(m_node);
    }
    bool is_valid() const { return m_type != node_type::__Invalid__; }
    node_csp get_node() const { return m_node; }
    template<typename _TRet_>
    _TRet_ visit(std::function<_TRet_(std::shared_ptr<const _T_>)>... _functors) const
    {
      return visit_impl(_functors...);
    }
  private:
    template<typename _TRet_, typename _T1_, typename... _TOther_>
    _TRet_ visit_impl(std::function<_TRet_(std::shared_ptr<const _T1_>)> _f1, std::function<_TRet_(std::shared_ptr<const _TOther_>)>... _others) const
    {
      std::shared_ptr<const _T1_> t = std::dynamic_pointer_cast<const _T1_>(m_node);
      if(t) return _f1(t);
      return visit_impl<_TRet_>(_others...);
    }
    template<typename _TRet_>
    _TRet_ visit_impl() const { throw_exception(exception_stage::unspecified, exception_code::internal_error, "No alternative found."); }
    node_csp m_node;
    node_type m_type;
  };
}

namespace gqlite
{
  template<>
  inline std::string to_string<oc::algebra::edge_directivity>(const oc::algebra::edge_directivity& _v)
  {
    switch(_v)
    {
      using enum oc::algebra::edge_directivity;
      case directed:
        return "directed";
      case undirected:
        return "undirected";
    }
    return "unknown";
  }
}

#endif
//END oc/algebra/node.h
//BEGIN oc/algebra/node_p.h


namespace gqlite::oc::algebra
{
  struct node::data
  {
    virtual ~data() {}
    node_type type;
  };
}
//END oc/algebra/node_p.h
//BEGIN oc/algebra/nodes.h
#ifndef _OC_ALGEBRA_NODES_H_
#define _OC_ALGEBRA_NODES_H_

#include <gqlite.h>



namespace gqlite::oc::algebra
{
  enum class match_modifier
  {
    none, distinct
  };
}

#define OC_ALGEBRA_GENERATE(_KLASS_NAME_, _MEMBER_DEF_)                                                                                             \
  class _KLASS_NAME_;                                                                                                                                       \
  typedef std::shared_ptr<const _KLASS_NAME_> _KLASS_NAME_ ## _csp;


namespace gqlite::oc::algebra
{
// Queries

#define OC_ALGEBRA_CREATE_NODES_MEMBERS(_KLASS_NAME_, F)                                    \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)

OC_ALGEBRA_GENERATE(create, OC_ALGEBRA_CREATE_NODES_MEMBERS)

#define OC_ALGEBRA_MATCH_NODES_MEMBERS(_KLASS_NAME_, F)                                     \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)  \
  F(_KLASS_NAME_, node_csp, where)

OC_ALGEBRA_GENERATE(match, OC_ALGEBRA_MATCH_NODES_MEMBERS)

// Statements

#define OC_ALGEBRA_NODES_LIST_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, nodes)

OC_ALGEBRA_GENERATE(statements, OC_ALGEBRA_NODES_LIST_MEMBERS)

#define OC_ALGEBRA_RETURN_MEMBERS(_KLASS_NAME_, F)                \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions) \
  F(_KLASS_NAME_, modifiers_csp, modifiers)

OC_ALGEBRA_GENERATE(return_statement, OC_ALGEBRA_RETURN_MEMBERS)

#define OC_ALGEBRA_WITH_MEMBERS(_KLASS_NAME_, F)                  \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions) \
  F(_KLASS_NAME_, modifiers_csp, modifiers)

OC_ALGEBRA_GENERATE(with, OC_ALGEBRA_WITH_MEMBERS)

#define OC_ALGEBRA_UNWIND_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, std::string, name)                \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(unwind, OC_ALGEBRA_UNWIND_MEMBERS)

#define OC_ALGEBRA_DELETE_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, bool, detach)                     \
  F(_KLASS_NAME_, std::vector<node_csp>, expressions)

OC_ALGEBRA_GENERATE(delete_statement, OC_ALGEBRA_DELETE_MEMBERS)

OC_ALGEBRA_GENERATE(set, OC_ALGEBRA_NODES_LIST_MEMBERS)
OC_ALGEBRA_GENERATE(remove, OC_ALGEBRA_NODES_LIST_MEMBERS)

#define OC_ALGEBRA_CALL_MEMBERS(_KLASS_NAME_, F)    \
  F(_KLASS_NAME_, std::string, name)                \
  F(_KLASS_NAME_, std::vector<node_csp>, arguments) \
  F(_KLASS_NAME_, std::vector<std::string>, yield)

OC_ALGEBRA_GENERATE(call, OC_ALGEBRA_CALL_MEMBERS)


#define OC_ALGEBRA_ORDER_BY_MEMBERS(_KLASS_NAME_, F)                  \
  F(_KLASS_NAME_, std::vector<order_by_expression_csp>, expressions)

OC_ALGEBRA_GENERATE(order_by, OC_ALGEBRA_ORDER_BY_MEMBERS)

#define OC_ALGEBRA_MODIFIERS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, skip)                     \
  F(_KLASS_NAME_, node_csp, limit)                    \
  F(_KLASS_NAME_, order_by_csp, order_by)

OC_ALGEBRA_GENERATE(modifiers, OC_ALGEBRA_MODIFIERS_MEMBERS)

// Order By Expression

#define OC_ALGEBRA_ORDER_BY_EXPRESSION_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, bool, asc)                          \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(order_by_expression, OC_ALGEBRA_ORDER_BY_EXPRESSION_MEMBERS)

// Set/remove Expressions

#define OC_ALGEBRA_SET_ADD_VALUE_MEMBERS(_KLASS_NAME_, F)       \
  F(_KLASS_NAME_, std::string, left)                            \
  F(_KLASS_NAME_, std::vector<std::string>, path)               \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(set_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)


#define OC_ALGEBRA_SET_EQUAL_MEMBERS(_KLASS_NAME_, F)                     \
  F(_KLASS_NAME_, std::string, left)                            \
  F(_KLASS_NAME_, std::vector<std::string>, path)               \
  F(_KLASS_NAME_, nocde_csp, expression)

OC_ALGEBRA_GENERATE(add_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)

#define OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS(_KLASS_NAME_, F)     \
  F(_KLASS_NAME_, std::string, left)                            \
  F(_KLASS_NAME_, std::vector<std::string>, path)

OC_ALGEBRA_GENERATE(remove_property, OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS)

#define OC_ALGEBRA_EDIT_LABELS_MEMBERS(_KLASS_NAME_, F)                   \
  F(_KLASS_NAME_, std::string, target)                                    \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(edit_labels, OC_ALGEBRA_EDIT_LABELS_MEMBERS)


// Values

#define OC_ALGEBRA_NO_MEMBERS(_KLASS_NAME_, F)

OC_ALGEBRA_GENERATE(end_of_list, OC_ALGEBRA_NO_MEMBERS)

#define OC_ALGEBRA_GRAPH_NODE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, node_csp, properties)

OC_ALGEBRA_GENERATE(graph_node, OC_ALGEBRA_GRAPH_NODE_MEMBERS)

#define OC_ALGEBRA_GRAPH_EDGE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, graph_node_csp, source)                       \
  F(_KLASS_NAME_, graph_node_csp, destination)                  \
  F(_KLASS_NAME_, edge_directivity, directivity)                \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, node_csp, properties)

OC_ALGEBRA_GENERATE(graph_edge, OC_ALGEBRA_GRAPH_EDGE_MEMBERS)

#define OC_ALGEBRA_VALUE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, gqlite::value, value)

OC_ALGEBRA_GENERATE(value, OC_ALGEBRA_VALUE_MEMBERS)


#define OC_ALGEBRA_MAP_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::unordered_map<GQLITE_LIST(std::string, node_csp)>, map)

OC_ALGEBRA_GENERATE(map, OC_ALGEBRA_MAP_MEMBERS)

#define OC_ALGEBRA_ARRAY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, array)

OC_ALGEBRA_GENERATE(array, OC_ALGEBRA_ARRAY_MEMBERS)


// Expressions

#define OC_ALGEBRA_NAMED_EXPRESSION(_KLASS_NAME_, F)      \
  F(_KLASS_NAME_, std::string, name)                      \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(named_expression, OC_ALGEBRA_NAMED_EXPRESSION)

#define OC_ALGEBRA_VARIABLE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, identifier)

OC_ALGEBRA_GENERATE(variable, OC_ALGEBRA_VARIABLE_MEMBERS)

#define OC_ALGEBRA_MEMBER_ACCESS_MEMBERS(_KLASS_NAME_, F)   \
  F(_KLASS_NAME_, node_csp, left)                           \
  F(_KLASS_NAME_, std::vector<std::string>, path)

OC_ALGEBRA_GENERATE(member_access, OC_ALGEBRA_MEMBER_ACCESS_MEMBERS)

#define OC_ALGEBRA_INDEXED_ACCESS_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, node_csp, left)                           \
  F(_KLASS_NAME_, node_csp, index)                          \
  F(_KLASS_NAME_, node_csp, end)

OC_ALGEBRA_GENERATE(indexed_access, OC_ALGEBRA_INDEXED_ACCESS_MEMBERS)

#define OC_ALGEBRA_HAS_LABELS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, left)                   \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(has_labels, OC_ALGEBRA_HAS_LABELS_MEMBERS)

#define OC_ALGEBRA_FUNCTION_CALL_EXPRESSION(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, std::string, name)                          \
  F(_KLASS_NAME_, std::vector<node_csp>, arguments)

OC_ALGEBRA_GENERATE(function_call, OC_ALGEBRA_FUNCTION_CALL_EXPRESSION)

#define OC_ALGEBRA_BINARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, left)                  \
  F(_KLASS_NAME_, node_csp, right)

OC_ALGEBRA_GENERATE(logical_and, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(logical_or, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(logical_xor, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_different, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_in, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_not_in, OC_ALGEBRA_BINARY_MEMBERS)

OC_ALGEBRA_GENERATE(addition, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(substraction, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(multiplication, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(division, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(modulo, OC_ALGEBRA_BINARY_MEMBERS)

#define OC_ALGEBRA_UNARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, value)

OC_ALGEBRA_GENERATE(logical_negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_null, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_not_null, OC_ALGEBRA_UNARY_MEMBERS)

}

#undef OC_ALGEBRA_GENERATE

#define OC_ALGEBRA_GENERATE_CONSTRUCTOR_ARGUMENT(_KLASS_NAME_, _TYPE_, _NAME_)      \
  _TYPE_ _ ## _NAME_,

#define OC_ALGEBRA_GENERATE_ACCESSOR_DECLARATION(_KLASS_NAME_, _TYPE_, _NAME_,...)  \
  _TYPE_ get_ ## _NAME_() const;

#define OC_ALGEBRA_GENERATE(_KLASS_NAME_, _MEMBER_DEF_)                                                                                         \
  class _KLASS_NAME_ : public node                                                                                                              \
  {                                                                                                                                             \
  public:                                                                                                                                       \
    _KLASS_NAME_(_MEMBER_DEF_(_KLASS_NAME_, OC_ALGEBRA_GENERATE_CONSTRUCTOR_ARGUMENT) void* _ = nullptr);                                       \
    ~_KLASS_NAME_();                                                                                                                            \
  public:                                                                                                                                       \
    _MEMBER_DEF_(_KLASS_NAME_, OC_ALGEBRA_GENERATE_ACCESSOR_DECLARATION);                                                                       \
    using node::accept;                                                                                                                         \
    bool equals(const std::shared_ptr<const node>& _node) const override;                                                                       \
  private:                                                                                                                                      \
    void accept(details::abstract_node_visitor_adaptor* _node, void* _r, void* _parameter) const override;                                      \
  private:                                                                                                                                      \
    struct data;                                                                                                                                \
  };

namespace gqlite::oc::algebra
{
// Queries

#define OC_ALGEBRA_CREATE_NODES_MEMBERS(_KLASS_NAME_, F)                                    \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)

OC_ALGEBRA_GENERATE(create, OC_ALGEBRA_CREATE_NODES_MEMBERS)

#define OC_ALGEBRA_MATCH_NODES_MEMBERS(_KLASS_NAME_, F)                                     \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)  \
  F(_KLASS_NAME_, node_csp, where)

OC_ALGEBRA_GENERATE(match, OC_ALGEBRA_MATCH_NODES_MEMBERS)

// Statements

#define OC_ALGEBRA_NODES_LIST_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, nodes)

OC_ALGEBRA_GENERATE(statements, OC_ALGEBRA_NODES_LIST_MEMBERS)

#define OC_ALGEBRA_RETURN_MEMBERS(_KLASS_NAME_, F)                \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions) \
  F(_KLASS_NAME_, modifiers_csp, modifiers)

OC_ALGEBRA_GENERATE(return_statement, OC_ALGEBRA_RETURN_MEMBERS)

#define OC_ALGEBRA_WITH_MEMBERS(_KLASS_NAME_, F)                  \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions) \
  F(_KLASS_NAME_, modifiers_csp, modifiers)

OC_ALGEBRA_GENERATE(with, OC_ALGEBRA_WITH_MEMBERS)

#define OC_ALGEBRA_UNWIND_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, std::string, name)                \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(unwind, OC_ALGEBRA_UNWIND_MEMBERS)

#define OC_ALGEBRA_DELETE_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, bool, detach)                     \
  F(_KLASS_NAME_, std::vector<node_csp>, expressions)

OC_ALGEBRA_GENERATE(delete_statement, OC_ALGEBRA_DELETE_MEMBERS)

OC_ALGEBRA_GENERATE(set, OC_ALGEBRA_NODES_LIST_MEMBERS)
OC_ALGEBRA_GENERATE(remove, OC_ALGEBRA_NODES_LIST_MEMBERS)

#define OC_ALGEBRA_CALL_MEMBERS(_KLASS_NAME_, F)    \
  F(_KLASS_NAME_, std::string, name)                \
  F(_KLASS_NAME_, std::vector<node_csp>, arguments) \
  F(_KLASS_NAME_, std::vector<std::string>, yield)

OC_ALGEBRA_GENERATE(call, OC_ALGEBRA_CALL_MEMBERS)


#define OC_ALGEBRA_ORDER_BY_MEMBERS(_KLASS_NAME_, F)                  \
  F(_KLASS_NAME_, std::vector<order_by_expression_csp>, expressions)

OC_ALGEBRA_GENERATE(order_by, OC_ALGEBRA_ORDER_BY_MEMBERS)

#define OC_ALGEBRA_MODIFIERS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, skip)                     \
  F(_KLASS_NAME_, node_csp, limit)                    \
  F(_KLASS_NAME_, order_by_csp, order_by)

OC_ALGEBRA_GENERATE(modifiers, OC_ALGEBRA_MODIFIERS_MEMBERS)

// Order By Expression

#define OC_ALGEBRA_ORDER_BY_EXPRESSION_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, bool, asc)                          \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(order_by_expression, OC_ALGEBRA_ORDER_BY_EXPRESSION_MEMBERS)

// Set/remove Expressions

#define OC_ALGEBRA_SET_ADD_VALUE_MEMBERS(_KLASS_NAME_, F)       \
  F(_KLASS_NAME_, std::string, left)                            \
  F(_KLASS_NAME_, std::vector<std::string>, path)               \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(set_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)


#define OC_ALGEBRA_SET_EQUAL_MEMBERS(_KLASS_NAME_, F)                     \
  F(_KLASS_NAME_, std::string, left)                            \
  F(_KLASS_NAME_, std::vector<std::string>, path)               \
  F(_KLASS_NAME_, nocde_csp, expression)

OC_ALGEBRA_GENERATE(add_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)

#define OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS(_KLASS_NAME_, F)     \
  F(_KLASS_NAME_, std::string, left)                            \
  F(_KLASS_NAME_, std::vector<std::string>, path)

OC_ALGEBRA_GENERATE(remove_property, OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS)

#define OC_ALGEBRA_EDIT_LABELS_MEMBERS(_KLASS_NAME_, F)                   \
  F(_KLASS_NAME_, std::string, target)                                    \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(edit_labels, OC_ALGEBRA_EDIT_LABELS_MEMBERS)


// Values

#define OC_ALGEBRA_NO_MEMBERS(_KLASS_NAME_, F)

OC_ALGEBRA_GENERATE(end_of_list, OC_ALGEBRA_NO_MEMBERS)

#define OC_ALGEBRA_GRAPH_NODE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, node_csp, properties)

OC_ALGEBRA_GENERATE(graph_node, OC_ALGEBRA_GRAPH_NODE_MEMBERS)

#define OC_ALGEBRA_GRAPH_EDGE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, graph_node_csp, source)                       \
  F(_KLASS_NAME_, graph_node_csp, destination)                  \
  F(_KLASS_NAME_, edge_directivity, directivity)                \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, node_csp, properties)

OC_ALGEBRA_GENERATE(graph_edge, OC_ALGEBRA_GRAPH_EDGE_MEMBERS)

#define OC_ALGEBRA_VALUE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, gqlite::value, value)

OC_ALGEBRA_GENERATE(value, OC_ALGEBRA_VALUE_MEMBERS)


#define OC_ALGEBRA_MAP_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::unordered_map<GQLITE_LIST(std::string, node_csp)>, map)

OC_ALGEBRA_GENERATE(map, OC_ALGEBRA_MAP_MEMBERS)

#define OC_ALGEBRA_ARRAY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, array)

OC_ALGEBRA_GENERATE(array, OC_ALGEBRA_ARRAY_MEMBERS)


// Expressions

#define OC_ALGEBRA_NAMED_EXPRESSION(_KLASS_NAME_, F)      \
  F(_KLASS_NAME_, std::string, name)                      \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(named_expression, OC_ALGEBRA_NAMED_EXPRESSION)

#define OC_ALGEBRA_VARIABLE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, identifier)

OC_ALGEBRA_GENERATE(variable, OC_ALGEBRA_VARIABLE_MEMBERS)

#define OC_ALGEBRA_MEMBER_ACCESS_MEMBERS(_KLASS_NAME_, F)   \
  F(_KLASS_NAME_, node_csp, left)                           \
  F(_KLASS_NAME_, std::vector<std::string>, path)

OC_ALGEBRA_GENERATE(member_access, OC_ALGEBRA_MEMBER_ACCESS_MEMBERS)

#define OC_ALGEBRA_INDEXED_ACCESS_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, node_csp, left)                           \
  F(_KLASS_NAME_, node_csp, index)                          \
  F(_KLASS_NAME_, node_csp, end)

OC_ALGEBRA_GENERATE(indexed_access, OC_ALGEBRA_INDEXED_ACCESS_MEMBERS)

#define OC_ALGEBRA_HAS_LABELS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, left)                   \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(has_labels, OC_ALGEBRA_HAS_LABELS_MEMBERS)

#define OC_ALGEBRA_FUNCTION_CALL_EXPRESSION(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, std::string, name)                          \
  F(_KLASS_NAME_, std::vector<node_csp>, arguments)

OC_ALGEBRA_GENERATE(function_call, OC_ALGEBRA_FUNCTION_CALL_EXPRESSION)

#define OC_ALGEBRA_BINARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, left)                  \
  F(_KLASS_NAME_, node_csp, right)

OC_ALGEBRA_GENERATE(logical_and, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(logical_or, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(logical_xor, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_different, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_in, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_not_in, OC_ALGEBRA_BINARY_MEMBERS)

OC_ALGEBRA_GENERATE(addition, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(substraction, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(multiplication, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(division, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(modulo, OC_ALGEBRA_BINARY_MEMBERS)

#define OC_ALGEBRA_UNARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, value)

OC_ALGEBRA_GENERATE(logical_negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_null, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_not_null, OC_ALGEBRA_UNARY_MEMBERS)

}

#undef OC_ALGEBRA_GENERATE

#endif
//END oc/algebra/nodes.h
//BEGIN oc/algebra/abstract_node_visitor.h
#ifndef _OC_ALGEBRA_ABSTRACT_NODE_VISITOR_H_
#define _OC_ALGEBRA_ABSTRACT_NODE_VISITOR_H_

#include <any>
#include <vector>
#include <unordered_map>



namespace gqlite::oc::algebra
{
  namespace details
  {
    class abstract_node_visitor_adaptor
    {
    public:
      abstract_node_visitor_adaptor();
      ~abstract_node_visitor_adaptor();
  #define OC_ALGEBRA_GENERATE(_KLASS_NAME_, _MEMBER_DEF_)                         \
      virtual void call_visit(_KLASS_NAME_ ## _csp _node, void* _r, void* _parameter) = 0;
  // Queries

#define OC_ALGEBRA_CREATE_NODES_MEMBERS(_KLASS_NAME_, F)                                    \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)

OC_ALGEBRA_GENERATE(create, OC_ALGEBRA_CREATE_NODES_MEMBERS)

#define OC_ALGEBRA_MATCH_NODES_MEMBERS(_KLASS_NAME_, F)                                     \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)  \
  F(_KLASS_NAME_, node_csp, where)

OC_ALGEBRA_GENERATE(match, OC_ALGEBRA_MATCH_NODES_MEMBERS)

// Statements

#define OC_ALGEBRA_NODES_LIST_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, nodes)

OC_ALGEBRA_GENERATE(statements, OC_ALGEBRA_NODES_LIST_MEMBERS)

#define OC_ALGEBRA_RETURN_MEMBERS(_KLASS_NAME_, F)                \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions) \
  F(_KLASS_NAME_, modifiers_csp, modifiers)

OC_ALGEBRA_GENERATE(return_statement, OC_ALGEBRA_RETURN_MEMBERS)

#define OC_ALGEBRA_WITH_MEMBERS(_KLASS_NAME_, F)                  \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions) \
  F(_KLASS_NAME_, modifiers_csp, modifiers)

OC_ALGEBRA_GENERATE(with, OC_ALGEBRA_WITH_MEMBERS)

#define OC_ALGEBRA_UNWIND_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, std::string, name)                \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(unwind, OC_ALGEBRA_UNWIND_MEMBERS)

#define OC_ALGEBRA_DELETE_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, bool, detach)                     \
  F(_KLASS_NAME_, std::vector<node_csp>, expressions)

OC_ALGEBRA_GENERATE(delete_statement, OC_ALGEBRA_DELETE_MEMBERS)

OC_ALGEBRA_GENERATE(set, OC_ALGEBRA_NODES_LIST_MEMBERS)
OC_ALGEBRA_GENERATE(remove, OC_ALGEBRA_NODES_LIST_MEMBERS)

#define OC_ALGEBRA_CALL_MEMBERS(_KLASS_NAME_, F)    \
  F(_KLASS_NAME_, std::string, name)                \
  F(_KLASS_NAME_, std::vector<node_csp>, arguments) \
  F(_KLASS_NAME_, std::vector<std::string>, yield)

OC_ALGEBRA_GENERATE(call, OC_ALGEBRA_CALL_MEMBERS)


#define OC_ALGEBRA_ORDER_BY_MEMBERS(_KLASS_NAME_, F)                  \
  F(_KLASS_NAME_, std::vector<order_by_expression_csp>, expressions)

OC_ALGEBRA_GENERATE(order_by, OC_ALGEBRA_ORDER_BY_MEMBERS)

#define OC_ALGEBRA_MODIFIERS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, skip)                     \
  F(_KLASS_NAME_, node_csp, limit)                    \
  F(_KLASS_NAME_, order_by_csp, order_by)

OC_ALGEBRA_GENERATE(modifiers, OC_ALGEBRA_MODIFIERS_MEMBERS)

// Order By Expression

#define OC_ALGEBRA_ORDER_BY_EXPRESSION_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, bool, asc)                          \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(order_by_expression, OC_ALGEBRA_ORDER_BY_EXPRESSION_MEMBERS)

// Set/remove Expressions

#define OC_ALGEBRA_SET_ADD_VALUE_MEMBERS(_KLASS_NAME_, F)       \
  F(_KLASS_NAME_, std::string, left)                            \
  F(_KLASS_NAME_, std::vector<std::string>, path)               \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(set_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)


#define OC_ALGEBRA_SET_EQUAL_MEMBERS(_KLASS_NAME_, F)                     \
  F(_KLASS_NAME_, std::string, left)                            \
  F(_KLASS_NAME_, std::vector<std::string>, path)               \
  F(_KLASS_NAME_, nocde_csp, expression)

OC_ALGEBRA_GENERATE(add_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)

#define OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS(_KLASS_NAME_, F)     \
  F(_KLASS_NAME_, std::string, left)                            \
  F(_KLASS_NAME_, std::vector<std::string>, path)

OC_ALGEBRA_GENERATE(remove_property, OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS)

#define OC_ALGEBRA_EDIT_LABELS_MEMBERS(_KLASS_NAME_, F)                   \
  F(_KLASS_NAME_, std::string, target)                                    \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(edit_labels, OC_ALGEBRA_EDIT_LABELS_MEMBERS)


// Values

#define OC_ALGEBRA_NO_MEMBERS(_KLASS_NAME_, F)

OC_ALGEBRA_GENERATE(end_of_list, OC_ALGEBRA_NO_MEMBERS)

#define OC_ALGEBRA_GRAPH_NODE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, node_csp, properties)

OC_ALGEBRA_GENERATE(graph_node, OC_ALGEBRA_GRAPH_NODE_MEMBERS)

#define OC_ALGEBRA_GRAPH_EDGE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, graph_node_csp, source)                       \
  F(_KLASS_NAME_, graph_node_csp, destination)                  \
  F(_KLASS_NAME_, edge_directivity, directivity)                \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, node_csp, properties)

OC_ALGEBRA_GENERATE(graph_edge, OC_ALGEBRA_GRAPH_EDGE_MEMBERS)

#define OC_ALGEBRA_VALUE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, gqlite::value, value)

OC_ALGEBRA_GENERATE(value, OC_ALGEBRA_VALUE_MEMBERS)


#define OC_ALGEBRA_MAP_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::unordered_map<GQLITE_LIST(std::string, node_csp)>, map)

OC_ALGEBRA_GENERATE(map, OC_ALGEBRA_MAP_MEMBERS)

#define OC_ALGEBRA_ARRAY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, array)

OC_ALGEBRA_GENERATE(array, OC_ALGEBRA_ARRAY_MEMBERS)


// Expressions

#define OC_ALGEBRA_NAMED_EXPRESSION(_KLASS_NAME_, F)      \
  F(_KLASS_NAME_, std::string, name)                      \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(named_expression, OC_ALGEBRA_NAMED_EXPRESSION)

#define OC_ALGEBRA_VARIABLE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, identifier)

OC_ALGEBRA_GENERATE(variable, OC_ALGEBRA_VARIABLE_MEMBERS)

#define OC_ALGEBRA_MEMBER_ACCESS_MEMBERS(_KLASS_NAME_, F)   \
  F(_KLASS_NAME_, node_csp, left)                           \
  F(_KLASS_NAME_, std::vector<std::string>, path)

OC_ALGEBRA_GENERATE(member_access, OC_ALGEBRA_MEMBER_ACCESS_MEMBERS)

#define OC_ALGEBRA_INDEXED_ACCESS_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, node_csp, left)                           \
  F(_KLASS_NAME_, node_csp, index)                          \
  F(_KLASS_NAME_, node_csp, end)

OC_ALGEBRA_GENERATE(indexed_access, OC_ALGEBRA_INDEXED_ACCESS_MEMBERS)

#define OC_ALGEBRA_HAS_LABELS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, left)                   \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(has_labels, OC_ALGEBRA_HAS_LABELS_MEMBERS)

#define OC_ALGEBRA_FUNCTION_CALL_EXPRESSION(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, std::string, name)                          \
  F(_KLASS_NAME_, std::vector<node_csp>, arguments)

OC_ALGEBRA_GENERATE(function_call, OC_ALGEBRA_FUNCTION_CALL_EXPRESSION)

#define OC_ALGEBRA_BINARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, left)                  \
  F(_KLASS_NAME_, node_csp, right)

OC_ALGEBRA_GENERATE(logical_and, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(logical_or, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(logical_xor, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_different, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_in, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_not_in, OC_ALGEBRA_BINARY_MEMBERS)

OC_ALGEBRA_GENERATE(addition, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(substraction, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(multiplication, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(division, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(modulo, OC_ALGEBRA_BINARY_MEMBERS)

#define OC_ALGEBRA_UNARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, value)

OC_ALGEBRA_GENERATE(logical_negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_null, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_not_null, OC_ALGEBRA_UNARY_MEMBERS)

  #undef OC_ALGEBRA_GENERATE
    // protected:
      // void accept(node_csp _node, void* _r, void* _parameter)
      // {
      //   return accept(_r, _node.ge(), _parameter);
      // }
      // void accept(const node* _node, void* _r, void* _parameter)
      // {
      //   if(_node)
      //   {
      //     _node->accept(this, _r, _parameter);
      //   }
      // }
    };    
  }
  
  template<typename _TR_, typename... _TArgs_>
  class abstract_node_visitor : details::abstract_node_visitor_adaptor
  {
    friend class node;
  public:
    using ParametersTuple = std::tuple<_TArgs_...>;
    static constexpr std::size_t ParametersCount = sizeof...(_TArgs_);
    using ReturnType = _TR_;
  public:
    abstract_node_visitor() {}
    ~abstract_node_visitor() {}
  public:
    /**
     * Call this function to start visiting
     */
    _TR_ start(node_csp _node, const _TArgs_&... _args)
    {
      return accept(_node, _args...);
    }
#define OC_ALGEBRA_GENERATE(_KLASS_NAME_, _MEMBER_DEF_)                                           \
  private:                                                                                                \
    virtual _TR_ visit(_KLASS_NAME_ ## _csp _node, const _TArgs_&... _parameter) = 0;                      \
    virtual void call_visit(_KLASS_NAME_ ## _csp _node, void* _r, void* _parameter) override               \
    {                                                                                                     \
      typedef _TR_ (abstract_node_visitor::* F)(_KLASS_NAME_ ## _csp _node, const _TArgs_&...);              \
      *reinterpret_cast<_TR_*>(_r) = std::apply(F(&abstract_node_visitor::visit), std::tuple_cat(std::make_tuple(this, _node),   \
                                                *reinterpret_cast<ParametersTuple*>(_parameter)));        \
    }
// Queries

#define OC_ALGEBRA_CREATE_NODES_MEMBERS(_KLASS_NAME_, F)                                    \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)

OC_ALGEBRA_GENERATE(create, OC_ALGEBRA_CREATE_NODES_MEMBERS)

#define OC_ALGEBRA_MATCH_NODES_MEMBERS(_KLASS_NAME_, F)                                     \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)  \
  F(_KLASS_NAME_, node_csp, where)

OC_ALGEBRA_GENERATE(match, OC_ALGEBRA_MATCH_NODES_MEMBERS)

// Statements

#define OC_ALGEBRA_NODES_LIST_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, nodes)

OC_ALGEBRA_GENERATE(statements, OC_ALGEBRA_NODES_LIST_MEMBERS)

#define OC_ALGEBRA_RETURN_MEMBERS(_KLASS_NAME_, F)                \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions) \
  F(_KLASS_NAME_, modifiers_csp, modifiers)

OC_ALGEBRA_GENERATE(return_statement, OC_ALGEBRA_RETURN_MEMBERS)

#define OC_ALGEBRA_WITH_MEMBERS(_KLASS_NAME_, F)                  \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions) \
  F(_KLASS_NAME_, modifiers_csp, modifiers)

OC_ALGEBRA_GENERATE(with, OC_ALGEBRA_WITH_MEMBERS)

#define OC_ALGEBRA_UNWIND_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, std::string, name)                \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(unwind, OC_ALGEBRA_UNWIND_MEMBERS)

#define OC_ALGEBRA_DELETE_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, bool, detach)                     \
  F(_KLASS_NAME_, std::vector<node_csp>, expressions)

OC_ALGEBRA_GENERATE(delete_statement, OC_ALGEBRA_DELETE_MEMBERS)

OC_ALGEBRA_GENERATE(set, OC_ALGEBRA_NODES_LIST_MEMBERS)
OC_ALGEBRA_GENERATE(remove, OC_ALGEBRA_NODES_LIST_MEMBERS)

#define OC_ALGEBRA_CALL_MEMBERS(_KLASS_NAME_, F)    \
  F(_KLASS_NAME_, std::string, name)                \
  F(_KLASS_NAME_, std::vector<node_csp>, arguments) \
  F(_KLASS_NAME_, std::vector<std::string>, yield)

OC_ALGEBRA_GENERATE(call, OC_ALGEBRA_CALL_MEMBERS)


#define OC_ALGEBRA_ORDER_BY_MEMBERS(_KLASS_NAME_, F)                  \
  F(_KLASS_NAME_, std::vector<order_by_expression_csp>, expressions)

OC_ALGEBRA_GENERATE(order_by, OC_ALGEBRA_ORDER_BY_MEMBERS)

#define OC_ALGEBRA_MODIFIERS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, skip)                     \
  F(_KLASS_NAME_, node_csp, limit)                    \
  F(_KLASS_NAME_, order_by_csp, order_by)

OC_ALGEBRA_GENERATE(modifiers, OC_ALGEBRA_MODIFIERS_MEMBERS)

// Order By Expression

#define OC_ALGEBRA_ORDER_BY_EXPRESSION_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, bool, asc)                          \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(order_by_expression, OC_ALGEBRA_ORDER_BY_EXPRESSION_MEMBERS)

// Set/remove Expressions

#define OC_ALGEBRA_SET_ADD_VALUE_MEMBERS(_KLASS_NAME_, F)       \
  F(_KLASS_NAME_, std::string, left)                            \
  F(_KLASS_NAME_, std::vector<std::string>, path)               \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(set_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)


#define OC_ALGEBRA_SET_EQUAL_MEMBERS(_KLASS_NAME_, F)                     \
  F(_KLASS_NAME_, std::string, left)                            \
  F(_KLASS_NAME_, std::vector<std::string>, path)               \
  F(_KLASS_NAME_, nocde_csp, expression)

OC_ALGEBRA_GENERATE(add_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)

#define OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS(_KLASS_NAME_, F)     \
  F(_KLASS_NAME_, std::string, left)                            \
  F(_KLASS_NAME_, std::vector<std::string>, path)

OC_ALGEBRA_GENERATE(remove_property, OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS)

#define OC_ALGEBRA_EDIT_LABELS_MEMBERS(_KLASS_NAME_, F)                   \
  F(_KLASS_NAME_, std::string, target)                                    \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(edit_labels, OC_ALGEBRA_EDIT_LABELS_MEMBERS)


// Values

#define OC_ALGEBRA_NO_MEMBERS(_KLASS_NAME_, F)

OC_ALGEBRA_GENERATE(end_of_list, OC_ALGEBRA_NO_MEMBERS)

#define OC_ALGEBRA_GRAPH_NODE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, node_csp, properties)

OC_ALGEBRA_GENERATE(graph_node, OC_ALGEBRA_GRAPH_NODE_MEMBERS)

#define OC_ALGEBRA_GRAPH_EDGE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, graph_node_csp, source)                       \
  F(_KLASS_NAME_, graph_node_csp, destination)                  \
  F(_KLASS_NAME_, edge_directivity, directivity)                \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, node_csp, properties)

OC_ALGEBRA_GENERATE(graph_edge, OC_ALGEBRA_GRAPH_EDGE_MEMBERS)

#define OC_ALGEBRA_VALUE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, gqlite::value, value)

OC_ALGEBRA_GENERATE(value, OC_ALGEBRA_VALUE_MEMBERS)


#define OC_ALGEBRA_MAP_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::unordered_map<GQLITE_LIST(std::string, node_csp)>, map)

OC_ALGEBRA_GENERATE(map, OC_ALGEBRA_MAP_MEMBERS)

#define OC_ALGEBRA_ARRAY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, array)

OC_ALGEBRA_GENERATE(array, OC_ALGEBRA_ARRAY_MEMBERS)


// Expressions

#define OC_ALGEBRA_NAMED_EXPRESSION(_KLASS_NAME_, F)      \
  F(_KLASS_NAME_, std::string, name)                      \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(named_expression, OC_ALGEBRA_NAMED_EXPRESSION)

#define OC_ALGEBRA_VARIABLE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, identifier)

OC_ALGEBRA_GENERATE(variable, OC_ALGEBRA_VARIABLE_MEMBERS)

#define OC_ALGEBRA_MEMBER_ACCESS_MEMBERS(_KLASS_NAME_, F)   \
  F(_KLASS_NAME_, node_csp, left)                           \
  F(_KLASS_NAME_, std::vector<std::string>, path)

OC_ALGEBRA_GENERATE(member_access, OC_ALGEBRA_MEMBER_ACCESS_MEMBERS)

#define OC_ALGEBRA_INDEXED_ACCESS_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, node_csp, left)                           \
  F(_KLASS_NAME_, node_csp, index)                          \
  F(_KLASS_NAME_, node_csp, end)

OC_ALGEBRA_GENERATE(indexed_access, OC_ALGEBRA_INDEXED_ACCESS_MEMBERS)

#define OC_ALGEBRA_HAS_LABELS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, left)                   \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(has_labels, OC_ALGEBRA_HAS_LABELS_MEMBERS)

#define OC_ALGEBRA_FUNCTION_CALL_EXPRESSION(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, std::string, name)                          \
  F(_KLASS_NAME_, std::vector<node_csp>, arguments)

OC_ALGEBRA_GENERATE(function_call, OC_ALGEBRA_FUNCTION_CALL_EXPRESSION)

#define OC_ALGEBRA_BINARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, left)                  \
  F(_KLASS_NAME_, node_csp, right)

OC_ALGEBRA_GENERATE(logical_and, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(logical_or, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(logical_xor, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_different, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_in, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_not_in, OC_ALGEBRA_BINARY_MEMBERS)

OC_ALGEBRA_GENERATE(addition, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(substraction, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(multiplication, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(division, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(modulo, OC_ALGEBRA_BINARY_MEMBERS)

#define OC_ALGEBRA_UNARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, value)

OC_ALGEBRA_GENERATE(logical_negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_null, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_not_null, OC_ALGEBRA_UNARY_MEMBERS)

#undef OC_ALGEBRA_GENERATE
  protected:
    _TR_ accept(node_csp _node, const _TArgs_... _arguments)
    {
      return accept(_node.get(), _arguments...);
    }
    _TR_ accept(const node* _node, const _TArgs_... _arguments)
    {
      _TR_ r;
      if(_node)
      {
        ParametersTuple pt = std::make_tuple(_arguments...);
        _node->accept(this, &r, &pt);
      }
      return r;
    }
  };
  
  template<typename... _TArgs_>
  class abstract_node_visitor<void, _TArgs_...> : details::abstract_node_visitor_adaptor
  {
    friend class node;
  public:
    using ParametersTuple = std::tuple<_TArgs_...>;
    static constexpr std::size_t ParametersCount = sizeof...(_TArgs_);
    using ReturnType = void;
  public:
    abstract_node_visitor() {}
    ~abstract_node_visitor() {}
  public:
    /**
     * Call this function to start visiting
     */
    void start(node_csp _node, const _TArgs_&... _args)
    {
      accept(_node, _args...);
    }
#define OC_ALGEBRA_GENERATE(_KLASS_NAME_, _MEMBER_DEF_)                                                                 \
  private:                                                                                                                      \
    virtual void visit(_KLASS_NAME_ ## _csp _node, const _TArgs_&... _parameter) = 0;                                            \
    virtual void call_visit(_KLASS_NAME_ ## _csp _node, void*, void* _parameter) override                                        \
    {                                                                                                                           \
      typedef void (abstract_node_visitor::* F)(_KLASS_NAME_ ## _csp _node, const _TArgs_&...);                                    \
      std::apply(F(&abstract_node_visitor::visit), std::tuple_cat(std::make_tuple(this, _node), *reinterpret_cast<ParametersTuple*>(_parameter) ));    \
    }
// Queries

#define OC_ALGEBRA_CREATE_NODES_MEMBERS(_KLASS_NAME_, F)                                    \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)

OC_ALGEBRA_GENERATE(create, OC_ALGEBRA_CREATE_NODES_MEMBERS)

#define OC_ALGEBRA_MATCH_NODES_MEMBERS(_KLASS_NAME_, F)                                     \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)  \
  F(_KLASS_NAME_, node_csp, where)

OC_ALGEBRA_GENERATE(match, OC_ALGEBRA_MATCH_NODES_MEMBERS)

// Statements

#define OC_ALGEBRA_NODES_LIST_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, nodes)

OC_ALGEBRA_GENERATE(statements, OC_ALGEBRA_NODES_LIST_MEMBERS)

#define OC_ALGEBRA_RETURN_MEMBERS(_KLASS_NAME_, F)                \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions) \
  F(_KLASS_NAME_, modifiers_csp, modifiers)

OC_ALGEBRA_GENERATE(return_statement, OC_ALGEBRA_RETURN_MEMBERS)

#define OC_ALGEBRA_WITH_MEMBERS(_KLASS_NAME_, F)                  \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions) \
  F(_KLASS_NAME_, modifiers_csp, modifiers)

OC_ALGEBRA_GENERATE(with, OC_ALGEBRA_WITH_MEMBERS)

#define OC_ALGEBRA_UNWIND_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, std::string, name)                \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(unwind, OC_ALGEBRA_UNWIND_MEMBERS)

#define OC_ALGEBRA_DELETE_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, bool, detach)                     \
  F(_KLASS_NAME_, std::vector<node_csp>, expressions)

OC_ALGEBRA_GENERATE(delete_statement, OC_ALGEBRA_DELETE_MEMBERS)

OC_ALGEBRA_GENERATE(set, OC_ALGEBRA_NODES_LIST_MEMBERS)
OC_ALGEBRA_GENERATE(remove, OC_ALGEBRA_NODES_LIST_MEMBERS)

#define OC_ALGEBRA_CALL_MEMBERS(_KLASS_NAME_, F)    \
  F(_KLASS_NAME_, std::string, name)                \
  F(_KLASS_NAME_, std::vector<node_csp>, arguments) \
  F(_KLASS_NAME_, std::vector<std::string>, yield)

OC_ALGEBRA_GENERATE(call, OC_ALGEBRA_CALL_MEMBERS)


#define OC_ALGEBRA_ORDER_BY_MEMBERS(_KLASS_NAME_, F)                  \
  F(_KLASS_NAME_, std::vector<order_by_expression_csp>, expressions)

OC_ALGEBRA_GENERATE(order_by, OC_ALGEBRA_ORDER_BY_MEMBERS)

#define OC_ALGEBRA_MODIFIERS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, skip)                     \
  F(_KLASS_NAME_, node_csp, limit)                    \
  F(_KLASS_NAME_, order_by_csp, order_by)

OC_ALGEBRA_GENERATE(modifiers, OC_ALGEBRA_MODIFIERS_MEMBERS)

// Order By Expression

#define OC_ALGEBRA_ORDER_BY_EXPRESSION_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, bool, asc)                          \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(order_by_expression, OC_ALGEBRA_ORDER_BY_EXPRESSION_MEMBERS)

// Set/remove Expressions

#define OC_ALGEBRA_SET_ADD_VALUE_MEMBERS(_KLASS_NAME_, F)       \
  F(_KLASS_NAME_, std::string, left)                            \
  F(_KLASS_NAME_, std::vector<std::string>, path)               \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(set_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)


#define OC_ALGEBRA_SET_EQUAL_MEMBERS(_KLASS_NAME_, F)                     \
  F(_KLASS_NAME_, std::string, left)                            \
  F(_KLASS_NAME_, std::vector<std::string>, path)               \
  F(_KLASS_NAME_, nocde_csp, expression)

OC_ALGEBRA_GENERATE(add_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)

#define OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS(_KLASS_NAME_, F)     \
  F(_KLASS_NAME_, std::string, left)                            \
  F(_KLASS_NAME_, std::vector<std::string>, path)

OC_ALGEBRA_GENERATE(remove_property, OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS)

#define OC_ALGEBRA_EDIT_LABELS_MEMBERS(_KLASS_NAME_, F)                   \
  F(_KLASS_NAME_, std::string, target)                                    \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(edit_labels, OC_ALGEBRA_EDIT_LABELS_MEMBERS)


// Values

#define OC_ALGEBRA_NO_MEMBERS(_KLASS_NAME_, F)

OC_ALGEBRA_GENERATE(end_of_list, OC_ALGEBRA_NO_MEMBERS)

#define OC_ALGEBRA_GRAPH_NODE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, node_csp, properties)

OC_ALGEBRA_GENERATE(graph_node, OC_ALGEBRA_GRAPH_NODE_MEMBERS)

#define OC_ALGEBRA_GRAPH_EDGE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, graph_node_csp, source)                       \
  F(_KLASS_NAME_, graph_node_csp, destination)                  \
  F(_KLASS_NAME_, edge_directivity, directivity)                \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, node_csp, properties)

OC_ALGEBRA_GENERATE(graph_edge, OC_ALGEBRA_GRAPH_EDGE_MEMBERS)

#define OC_ALGEBRA_VALUE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, gqlite::value, value)

OC_ALGEBRA_GENERATE(value, OC_ALGEBRA_VALUE_MEMBERS)


#define OC_ALGEBRA_MAP_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::unordered_map<GQLITE_LIST(std::string, node_csp)>, map)

OC_ALGEBRA_GENERATE(map, OC_ALGEBRA_MAP_MEMBERS)

#define OC_ALGEBRA_ARRAY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, array)

OC_ALGEBRA_GENERATE(array, OC_ALGEBRA_ARRAY_MEMBERS)


// Expressions

#define OC_ALGEBRA_NAMED_EXPRESSION(_KLASS_NAME_, F)      \
  F(_KLASS_NAME_, std::string, name)                      \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(named_expression, OC_ALGEBRA_NAMED_EXPRESSION)

#define OC_ALGEBRA_VARIABLE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, identifier)

OC_ALGEBRA_GENERATE(variable, OC_ALGEBRA_VARIABLE_MEMBERS)

#define OC_ALGEBRA_MEMBER_ACCESS_MEMBERS(_KLASS_NAME_, F)   \
  F(_KLASS_NAME_, node_csp, left)                           \
  F(_KLASS_NAME_, std::vector<std::string>, path)

OC_ALGEBRA_GENERATE(member_access, OC_ALGEBRA_MEMBER_ACCESS_MEMBERS)

#define OC_ALGEBRA_INDEXED_ACCESS_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, node_csp, left)                           \
  F(_KLASS_NAME_, node_csp, index)                          \
  F(_KLASS_NAME_, node_csp, end)

OC_ALGEBRA_GENERATE(indexed_access, OC_ALGEBRA_INDEXED_ACCESS_MEMBERS)

#define OC_ALGEBRA_HAS_LABELS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, left)                   \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(has_labels, OC_ALGEBRA_HAS_LABELS_MEMBERS)

#define OC_ALGEBRA_FUNCTION_CALL_EXPRESSION(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, std::string, name)                          \
  F(_KLASS_NAME_, std::vector<node_csp>, arguments)

OC_ALGEBRA_GENERATE(function_call, OC_ALGEBRA_FUNCTION_CALL_EXPRESSION)

#define OC_ALGEBRA_BINARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, left)                  \
  F(_KLASS_NAME_, node_csp, right)

OC_ALGEBRA_GENERATE(logical_and, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(logical_or, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(logical_xor, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_different, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_in, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_not_in, OC_ALGEBRA_BINARY_MEMBERS)

OC_ALGEBRA_GENERATE(addition, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(substraction, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(multiplication, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(division, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(modulo, OC_ALGEBRA_BINARY_MEMBERS)

#define OC_ALGEBRA_UNARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, value)

OC_ALGEBRA_GENERATE(logical_negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_null, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_not_null, OC_ALGEBRA_UNARY_MEMBERS)

#undef OC_ALGEBRA_GENERATE
  protected:
    void accept(node_csp _node, const _TArgs_&... _arguments)
    {
      accept(_node.get(), _arguments...);
    }
    void accept(const node* _node, const _TArgs_&... _arguments)
    {
      if(_node)
      {
        ParametersTuple pt = std::make_tuple(_arguments...);
        _node->accept(this, nullptr, &pt);
      }
    }
  };
}

#endif
//END oc/algebra/abstract_node_visitor.h
//BEGIN oc/algebra/default_node_visitor.h






#define __OC_ALGEBRA_NODE_VISITOR_OVERLOAD(_NAME_, _I_)             \
  Return visit(_NAME_ ## CSP _node, const Parameter& _parameter);

#define OC_ALGEBRA_NODE_VISITOR_OVERLOAD(...)                       \
  GQLITE_FOREACH(__OC_ALGEBRA_NODE_VISITOR_OVERLOAD, __VA_ARGS__)

namespace gqlite::oc::algebra
{
  /**
   * @ingroup GQL_Algebra
   * Base abstract_node_visitor with empty implementation
   */
  template<typename _TR_, typename... _TArgs_>
  class default_node_visitor : public abstract_node_visitor<_TR_, _TArgs_...>
  {
  protected:
    virtual _TR_ visit_default(node_csp _node, const _TArgs_&...) = 0;
#define OC_ALGEBRA_GENERATE(_KLASS_NAME_, _MEMBER_DEF_)                               \
        _TR_ visit(_KLASS_NAME_ ## _csp _node, const _TArgs_&... _args) override       \
        {                                                                             \
          return visit_default(_node, _args...);                                      \
        }
// Queries

#define OC_ALGEBRA_CREATE_NODES_MEMBERS(_KLASS_NAME_, F)                                    \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)

OC_ALGEBRA_GENERATE(create, OC_ALGEBRA_CREATE_NODES_MEMBERS)

#define OC_ALGEBRA_MATCH_NODES_MEMBERS(_KLASS_NAME_, F)                                     \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)  \
  F(_KLASS_NAME_, node_csp, where)

OC_ALGEBRA_GENERATE(match, OC_ALGEBRA_MATCH_NODES_MEMBERS)

// Statements

#define OC_ALGEBRA_NODES_LIST_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, nodes)

OC_ALGEBRA_GENERATE(statements, OC_ALGEBRA_NODES_LIST_MEMBERS)

#define OC_ALGEBRA_RETURN_MEMBERS(_KLASS_NAME_, F)                \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions) \
  F(_KLASS_NAME_, modifiers_csp, modifiers)

OC_ALGEBRA_GENERATE(return_statement, OC_ALGEBRA_RETURN_MEMBERS)

#define OC_ALGEBRA_WITH_MEMBERS(_KLASS_NAME_, F)                  \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions) \
  F(_KLASS_NAME_, modifiers_csp, modifiers)

OC_ALGEBRA_GENERATE(with, OC_ALGEBRA_WITH_MEMBERS)

#define OC_ALGEBRA_UNWIND_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, std::string, name)                \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(unwind, OC_ALGEBRA_UNWIND_MEMBERS)

#define OC_ALGEBRA_DELETE_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, bool, detach)                     \
  F(_KLASS_NAME_, std::vector<node_csp>, expressions)

OC_ALGEBRA_GENERATE(delete_statement, OC_ALGEBRA_DELETE_MEMBERS)

OC_ALGEBRA_GENERATE(set, OC_ALGEBRA_NODES_LIST_MEMBERS)
OC_ALGEBRA_GENERATE(remove, OC_ALGEBRA_NODES_LIST_MEMBERS)

#define OC_ALGEBRA_CALL_MEMBERS(_KLASS_NAME_, F)    \
  F(_KLASS_NAME_, std::string, name)                \
  F(_KLASS_NAME_, std::vector<node_csp>, arguments) \
  F(_KLASS_NAME_, std::vector<std::string>, yield)

OC_ALGEBRA_GENERATE(call, OC_ALGEBRA_CALL_MEMBERS)


#define OC_ALGEBRA_ORDER_BY_MEMBERS(_KLASS_NAME_, F)                  \
  F(_KLASS_NAME_, std::vector<order_by_expression_csp>, expressions)

OC_ALGEBRA_GENERATE(order_by, OC_ALGEBRA_ORDER_BY_MEMBERS)

#define OC_ALGEBRA_MODIFIERS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, skip)                     \
  F(_KLASS_NAME_, node_csp, limit)                    \
  F(_KLASS_NAME_, order_by_csp, order_by)

OC_ALGEBRA_GENERATE(modifiers, OC_ALGEBRA_MODIFIERS_MEMBERS)

// Order By Expression

#define OC_ALGEBRA_ORDER_BY_EXPRESSION_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, bool, asc)                          \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(order_by_expression, OC_ALGEBRA_ORDER_BY_EXPRESSION_MEMBERS)

// Set/remove Expressions

#define OC_ALGEBRA_SET_ADD_VALUE_MEMBERS(_KLASS_NAME_, F)       \
  F(_KLASS_NAME_, std::string, left)                            \
  F(_KLASS_NAME_, std::vector<std::string>, path)               \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(set_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)


#define OC_ALGEBRA_SET_EQUAL_MEMBERS(_KLASS_NAME_, F)                     \
  F(_KLASS_NAME_, std::string, left)                            \
  F(_KLASS_NAME_, std::vector<std::string>, path)               \
  F(_KLASS_NAME_, nocde_csp, expression)

OC_ALGEBRA_GENERATE(add_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)

#define OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS(_KLASS_NAME_, F)     \
  F(_KLASS_NAME_, std::string, left)                            \
  F(_KLASS_NAME_, std::vector<std::string>, path)

OC_ALGEBRA_GENERATE(remove_property, OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS)

#define OC_ALGEBRA_EDIT_LABELS_MEMBERS(_KLASS_NAME_, F)                   \
  F(_KLASS_NAME_, std::string, target)                                    \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(edit_labels, OC_ALGEBRA_EDIT_LABELS_MEMBERS)


// Values

#define OC_ALGEBRA_NO_MEMBERS(_KLASS_NAME_, F)

OC_ALGEBRA_GENERATE(end_of_list, OC_ALGEBRA_NO_MEMBERS)

#define OC_ALGEBRA_GRAPH_NODE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, node_csp, properties)

OC_ALGEBRA_GENERATE(graph_node, OC_ALGEBRA_GRAPH_NODE_MEMBERS)

#define OC_ALGEBRA_GRAPH_EDGE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, graph_node_csp, source)                       \
  F(_KLASS_NAME_, graph_node_csp, destination)                  \
  F(_KLASS_NAME_, edge_directivity, directivity)                \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, node_csp, properties)

OC_ALGEBRA_GENERATE(graph_edge, OC_ALGEBRA_GRAPH_EDGE_MEMBERS)

#define OC_ALGEBRA_VALUE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, gqlite::value, value)

OC_ALGEBRA_GENERATE(value, OC_ALGEBRA_VALUE_MEMBERS)


#define OC_ALGEBRA_MAP_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::unordered_map<GQLITE_LIST(std::string, node_csp)>, map)

OC_ALGEBRA_GENERATE(map, OC_ALGEBRA_MAP_MEMBERS)

#define OC_ALGEBRA_ARRAY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, array)

OC_ALGEBRA_GENERATE(array, OC_ALGEBRA_ARRAY_MEMBERS)


// Expressions

#define OC_ALGEBRA_NAMED_EXPRESSION(_KLASS_NAME_, F)      \
  F(_KLASS_NAME_, std::string, name)                      \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(named_expression, OC_ALGEBRA_NAMED_EXPRESSION)

#define OC_ALGEBRA_VARIABLE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, identifier)

OC_ALGEBRA_GENERATE(variable, OC_ALGEBRA_VARIABLE_MEMBERS)

#define OC_ALGEBRA_MEMBER_ACCESS_MEMBERS(_KLASS_NAME_, F)   \
  F(_KLASS_NAME_, node_csp, left)                           \
  F(_KLASS_NAME_, std::vector<std::string>, path)

OC_ALGEBRA_GENERATE(member_access, OC_ALGEBRA_MEMBER_ACCESS_MEMBERS)

#define OC_ALGEBRA_INDEXED_ACCESS_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, node_csp, left)                           \
  F(_KLASS_NAME_, node_csp, index)                          \
  F(_KLASS_NAME_, node_csp, end)

OC_ALGEBRA_GENERATE(indexed_access, OC_ALGEBRA_INDEXED_ACCESS_MEMBERS)

#define OC_ALGEBRA_HAS_LABELS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, left)                   \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(has_labels, OC_ALGEBRA_HAS_LABELS_MEMBERS)

#define OC_ALGEBRA_FUNCTION_CALL_EXPRESSION(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, std::string, name)                          \
  F(_KLASS_NAME_, std::vector<node_csp>, arguments)

OC_ALGEBRA_GENERATE(function_call, OC_ALGEBRA_FUNCTION_CALL_EXPRESSION)

#define OC_ALGEBRA_BINARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, left)                  \
  F(_KLASS_NAME_, node_csp, right)

OC_ALGEBRA_GENERATE(logical_and, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(logical_or, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(logical_xor, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_different, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_in, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_not_in, OC_ALGEBRA_BINARY_MEMBERS)

OC_ALGEBRA_GENERATE(addition, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(substraction, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(multiplication, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(division, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(modulo, OC_ALGEBRA_BINARY_MEMBERS)

#define OC_ALGEBRA_UNARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, value)

OC_ALGEBRA_GENERATE(logical_negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_null, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_not_null, OC_ALGEBRA_UNARY_MEMBERS)

#undef OC_ALGEBRA_GENERATE
  };
}

//END oc/algebra/default_node_visitor.h
//BEGIN oc/algebra/visitors/expression_analyser.h


#include <ranges>
#include <unordered_map>




namespace gqlite::oc::algebra::visitors
{
  using expression_type = gqlite::oc::algebra::expression_type;
  /**
   * This class analyse an algebra to get expression types and wether they are constant expressions.
   */
  struct expression_analyser : public gqlite::oc::algebra::default_node_visitor<expression_info>
  {
    exception_stage stage;
    std::function<expression_type(const std::string&)> variables_f;
    expression_info visit_default(algebra::node_csp _node) override
    {
      throw_exception(stage, exception_code::unimplemented_error, "Unimplemented node {} in expression_analyser visitor", oc::algebra::node_type_name(_node->get_type()));
    }
    expression_info visit(algebra::value_csp _var) override
    {
      switch(_var->get_value().get_type())
      {
        case value_type::invalid:
          return {expression_type::empty, true };
        case value_type::boolean:
          return {expression_type::boolean, true };
        case value_type::integer:
          return {expression_type::integer, true };
        case value_type::floating_point:
          return {expression_type::floating_point, true };
        case value_type::string:
          return {expression_type::string, true };
        case value_type::map:
          return {expression_type::map, true };
        case value_type::vector:
          return {expression_type::vector, true };
      };
      throw_exception(exception_stage::unspecified, exception_code::internal_error, "Unknown value type.");
    }
    template<typename _T_>
    bool constant_nodes(const _T_& _nodes)
    {
      for(algebra::node_csp n : _nodes)
      {
        if(not start(n).constant)
        {
          return false;
        }
      }
      return true;
    }
    expression_info visit(algebra::array_csp _value) override
    {
      return {expression_type::vector, constant_nodes(_value->get_array())};
    }
    expression_info visit(algebra::map_csp _value) override
    {
      return {expression_type::map, constant_nodes(workarounds::views::values(_value->get_map()))};
    }
    expression_info visit(algebra::member_access_csp _ma) override
    {
      return {expression_type::value, start(_ma->get_left()).constant};
    }
    expression_info visit(algebra::indexed_access_csp _ia) override
    {
      return {expression_type::value, start(_ia->get_left()).constant};
    }
    expression_info visit(algebra::has_labels_csp) override
    {
      return {expression_type::boolean, false};
    }
#define GQLITE_ET_LOGICAL_OP(_NAME_)                              \
    expression_info visit(algebra::_NAME_ ## _csp _node) override \
    {                                                             \
      bool constant = start(_node->get_left()).constant           \
                  and start(_node->get_right()).constant;         \
      return {expression_type::boolean, constant};                \
    }
    GQLITE_ET_LOGICAL_OP(logical_and)
    GQLITE_ET_LOGICAL_OP(logical_or)
    GQLITE_ET_LOGICAL_OP(logical_xor)
    GQLITE_ET_LOGICAL_OP(relational_equal)
    GQLITE_ET_LOGICAL_OP(relational_different)
    GQLITE_ET_LOGICAL_OP(relational_inferior)
    GQLITE_ET_LOGICAL_OP(relational_superior)
    GQLITE_ET_LOGICAL_OP(relational_inferior_equal)
    GQLITE_ET_LOGICAL_OP(relational_superior_equal)
    GQLITE_ET_LOGICAL_OP(relational_in)
    GQLITE_ET_LOGICAL_OP(relational_not_in)
#define GQLITE_ET_LOGICAL_UNARY_OP(_NAME_)                          \
    expression_info visit(algebra::_NAME_ ## _csp _node) override   \
    {                                                               \
      bool constant = start(_node->get_value()).constant;           \
      return {expression_type::boolean, constant};                  \
    }
    GQLITE_ET_LOGICAL_UNARY_OP(is_null)
    GQLITE_ET_LOGICAL_UNARY_OP(is_not_null)
#define GQLITE_ET_BINARY_OP(_NAME_)                                                                       \
    expression_info visit(algebra::_NAME_ ## _csp _node) override                                         \
    {                                                                                                     \
      expression_info left = start(_node->get_left());                                                    \
      expression_info right = start(_node->get_right());                                                  \
      bool constant = left.constant and right.constant;                                                   \
      if(left.type == right.type) return {left.type, constant};                                           \
      if(left.type == expression_type::value or right.type == expression_type::value)                     \
        return {expression_type::value, constant};                                                        \
      if(left.type == expression_type::floating_point or right.type == expression_type::floating_point)   \
      {                                                                                                   \
        return {expression_type::floating_point, constant};                                               \
      }                                                                                                   \
      if(left.type == expression_type::string or right.type == expression_type::string)                   \
      {                                                                                                   \
        return {expression_type::string, constant};                                                       \
      }                                                                                                   \
      if(std::is_same_v<algebra::_NAME_ ## _csp, algebra::addition_csp>                                   \
            and left.type == expression_type::vector)                                                     \
      {                                                                                                   \
        return {expression_type::vector, constant};                                                       \
      }                                                                                                   \
      errors::invalid_argument_type(stage, "In binary operation.");                                       \
    }
    GQLITE_ET_BINARY_OP(addition)
    GQLITE_ET_BINARY_OP(substraction)
    GQLITE_ET_BINARY_OP(multiplication)
    GQLITE_ET_BINARY_OP(division)
    GQLITE_ET_BINARY_OP(modulo)

    expression_info visit(algebra::logical_negation_csp _node)
    {
      bool constant = start(_node->get_value()).constant;
      return {expression_type::boolean, constant};
    }
    expression_info visit(algebra::variable_csp _var) override
    {
      return {variables_f(_var->get_identifier()), false};
    }
    struct function_info
    {
      expression_type return_type;
      std::vector<expression_type> arguments_types;
      bool deterministic;
    };
    static std::unordered_multimap<std::string, function_info> create_functions()
    {
      std::unordered_multimap<std::string, function_info> f;
      using enum expression_type;
      using FI = function_info;
      using V = std::vector<expression_type>;
      f.emplace("head", FI{value, V{vector}, true});
      f.emplace("id", FI{integer, V{node}, true});
      f.emplace("id", FI{integer, V{edge}, true});
      f.emplace("keys", FI{vector, V{edge}, true});
      f.emplace("keys", FI{vector, V{node}, true});
      f.emplace("keys", FI{vector, V{map}, true});
      f.emplace("labels", FI{vector, V{node}, true});
      f.emplace("properties", FI{map, V{node}, true});
      f.emplace("properties", FI{map, V{edge}, true});
      f.emplace("properties", FI{map, V{map}, true});
      f.emplace("range", FI{string, V{integer, integer}, true});
      f.emplace("size", FI{integer, V{vector}, true});
      f.emplace("tail", FI{vector, V{vector}, true});
      f.emplace("toInteger", FI{integer, V{integer}, true});
      f.emplace("toInteger", FI{integer, V{floating_point}, true});
      f.emplace("type", FI{string, V{edge}, true});
      return f;
    }
    expression_info visit(algebra::function_call_csp _node) override
    {
      if(_node->get_name() == "coalesce")
      { // coalesce is a special case that can accept any value
        return {expression_type::value, constant_nodes(_node->get_arguments())};
      }
      // 1) Check if all arguments are constant and get their type
      bool constant_arguments = true;
      std::vector<expression_type> argument_types;
      for(algebra::node_csp arg : _node->get_arguments())
      {
        expression_info arg_ei = start(arg);
        argument_types.push_back(arg_ei.type);
        constant_arguments = constant_arguments and arg_ei.constant;
      }
      // 2) Find a matching function
      static std::unordered_multimap<std::string, function_info> functions = create_functions();
      auto it_functions = functions.equal_range(_node->get_name());
      for(auto it = it_functions.first; it != it_functions.second; ++it)
      {
        if(argument_types.size() == it->second.arguments_types.size())
        {
          bool match = true;
          for(std::size_t i = 0; i < argument_types.size(); ++i)
          {
            expression_type arg_type = argument_types[i];
            expression_type f_type = it->second.arguments_types[i];
            if(arg_type != f_type and arg_type != expression_type::value and f_type != expression_type::value and arg_type != expression_type::empty)
            {
              match = false;
            }
            break;
          }
          if(match) return {it->second.return_type, it->second.deterministic and constant_arguments};
        }
      }
      // 3) If no function was found, return an exception for unknown function or invalid arguments
      if(it_functions.first == it_functions.second)
      {
        throw_exception(stage, exception_code::unknown_function, "Function {} is unknown.", _node->get_name());
      } else {
        errors::invalid_argument_type(stage, "When calling function {}.", _node->get_name());
      }
    }


  };
}
//END oc/algebra/visitors/expression_analyser.h
//BEGIN oc/algebra/visitors/print.h


namespace gqlite::oc::algebra::visitors
{
  namespace details
  {
    template<typename _T_, typename = void>
    struct print_helper;
  }
  class print : public abstract_node_visitor<void>
  {
    template<typename _T_, typename = void>
    friend class details::print_helper;
  public:
    print();
    ~print();
#define OC_ALGEBRA_GENERATE(_KLASS_NAME_, _MEMBER_DEF_) \
  virtual void visit(_KLASS_NAME_ ## _csp _node) override;

#undef OC_ALGEBRA_GENERATE
  private:
    struct data;
    data* const d;
  };
}
//END oc/algebra/visitors/print.h
//BEGIN oc/algebra/visitors/stringify.h






namespace gqlite::oc::algebra::visitors
{
  struct stringify : public gqlite::oc::algebra::default_node_visitor<std::string>
  {
    std::string visit_default(algebra::node_csp _node) override
    {
      throw_exception(exception_stage::unspecified, exception_code::unimplemented_error, "Unimplemented node {} in stringify", oc::algebra::node_type_name(_node->get_type()));
    }
    std::string visit(algebra::value_csp _var) override
    {
      return _var->get_value().to_json();
    }
     std::string visit(algebra::array_csp _var) override
    {
      std::string r = "[";
      bool first = true;
      for(algebra::node_csp n : _var->get_array())
      {
        if(not first) r += ", ";
        r += start(n);
        first = false;
      }
      return r + "]";
    }
    std::string visit(algebra::map_csp _var) override
    {
      std::string r = "{";
      bool first = true;
      for(auto const& [k, n] : _var->get_map())
      {
        if(not first) r += ", ";
        r += k + ": " + start(n);
        first = false;
      }
      return r + "}";
    }
    std::string visit(algebra::member_access_csp _var) override
    {
      return format_string("{}.{}", start(_var->get_left()), string::join(_var->get_path(), "."));
    }
    std::string visit(algebra::indexed_access_csp _var) override
    {
      return format_string("{}[{}]", start(_var->get_left()), start(_var->get_index()));
    }
#define GQLITE_STRINGIFY_BIN_OP(_NAME_, _OP_)                                     \
    std::string visit(algebra::_NAME_ ## _csp _var) override                      \
    {                                                                             \
      return start(_var->get_left()) + " " # _OP_ " " + start(_var->get_right()); \
    }
    GQLITE_STRINGIFY_BIN_OP(logical_and, AND)
    GQLITE_STRINGIFY_BIN_OP(logical_or, OR)
    GQLITE_STRINGIFY_BIN_OP(logical_xor, XOR)
    GQLITE_STRINGIFY_BIN_OP(relational_in, IN)
    GQLITE_STRINGIFY_BIN_OP(relational_not_in, NOT IN)
#define GQLITE_STRINGIFY_UNARY_OP(_NAME_, _OP_)                                   \
    std::string visit(algebra::_NAME_ ## _csp _var) override                      \
    {                                                                             \
      return # _OP_ " " + start(_var->get_value());                                 \
    }
    GQLITE_STRINGIFY_UNARY_OP(logical_negation, NOT)
    std::string visit(algebra::variable_csp _var) override
    {
      return _var->get_identifier();
    }
    std::string visit(algebra::function_call_csp _var) override
    {
      std::string args;
      for(algebra::node_csp node : _var->get_arguments())
      {
        if(not args.empty()) args += ", ";
        args += start(node);
      }
      return format_string("{}({})", _var->get_name(), args);
    }
  };
}
//END oc/algebra/visitors/stringify.h
//BEGIN oc/parser.h
#include <tuple>



namespace gqlite::oc
{
  class lexer;
  class parser
  {
  public:
    parser(lexer* _lexer, const value_map& _bindings);
    ~parser();
    algebra::node_csp parse();
  private:
    struct data;
    data* const d;
  };
} // namespace gqlite::oc
//END oc/parser.h
//BEGIN backend.h




namespace gqlite
{
  class backend
  {
  public:
    virtual ~backend();
  public:
    virtual value execute_oc_query(oc::algebra::node_csp _node) = 0;
  };
}
//END backend.h
//BEGIN backends/sqlite_queries.h
//To update this file, run 'queries_gen.rb'
#include <sstream>

namespace gqlite::backends::sqlite_queries
{
  inline std::string edge_add_properties(const std::string& _graph_name, const std::string& _what, const std::string& _expr, const std::string& _path, const std::string& _edge_id)
  {
    std::stringstream stream;
    
stream << "UPDATE gqlite_";

stream << ( _graph_name );

stream << "_edges AS tblu SET properties=json_patch(tblu.properties, json_set('{}', \"";

stream << ( _path );

stream << "\", gqlite_jsonify(";

stream << ( _expr );

stream << "))) ";

stream << ( _what );

stream << " WHERE tblu.id = ";

stream << ( _edge_id );

    return stream.str();
  }
  inline std::string edge_count_by_node(const std::string& _graph_name)
  {
    std::stringstream stream;
    
stream << "SELECT count(*) FROM gqlite_";

stream << ( _graph_name );

stream << "_edges WHERE left=?001 or right=?001";

    return stream.str();
  }
  inline std::string edge_create(const std::string& _graph_name, const std::string& _values)
  {
    std::stringstream stream;
    
stream << "INSERT INTO gqlite_";

stream << ( _graph_name );

stream << "_edges (id, label, properties, left, right) ";

stream << ( _values );

stream << "\n"
"RETURNING id\n"
"";

    return stream.str();
  }
  inline std::string edge_delete(const std::string& _graph_name, const std::string& _what)
  {
    std::stringstream stream;
    
stream << "DELETE FROM gqlite_";

stream << ( _graph_name );

stream << "_edges WHERE id IN (";

stream << ( _what );

stream << ")";

    return stream.str();
  }
  inline std::string edge_delete_by_node(const std::string& _graph_name, const std::string& _what)
  {
    std::stringstream stream;
    
stream << "WITH source_delete AS NOT MATERIALIZED (";

stream << ( _what );

stream << ")\n"
"DELETE FROM gqlite_";

stream << ( _graph_name );

stream << "_edges WHERE left IN source_delete or right IN source_delete";

    return stream.str();
  }
  inline std::string edge_get_label_properties(const std::string& _graph_name)
  {
    std::stringstream stream;
    
stream << "SELECT label, properties FROM gqlite_";

stream << ( _graph_name );

stream << "_edges WHERE id = ?001";

    return stream.str();
  }
  inline std::string edge_set_property(const std::string& _graph_name, const std::string& _what, const std::string& _expr, const std::string& _path, const std::string& _edge_id)
  {
    std::stringstream stream;
    
stream << "UPDATE gqlite_";

stream << ( _graph_name );

stream << "_edges AS tblu SET properties=json_patch('{}', json_set(tblu.properties, \"";

stream << ( _path );

stream << "\", gqlite_jsonify(";

stream << ( _expr );

stream << "))) ";

stream << ( _what );

stream << " WHERE tblu.id = ";

stream << ( _edge_id );

    return stream.str();
  }
  inline std::string edge_remove_property(const std::string& _graph_name, const std::string& _what, const std::string& _path, const std::string& _edge_id)
  {
    std::stringstream stream;
    
stream << "UPDATE gqlite_";

stream << ( _graph_name );

stream << "_edges AS tblu SET properties=json_remove(tblu.properties, \"";

stream << ( _path );

stream << "\") ";

stream << ( _what );

stream << " WHERE tblu.id = ";

stream << ( _edge_id );

    return stream.str();
  }
  inline std::string function_labels(const std::string& _graph_name, const std::string& _node_id)
  {
    std::stringstream stream;
    
stream << "(SELECT json_group_array(labels.label)\n"
"            FROM gqlite_";

stream << ( _graph_name );

stream << "_labels as l\n"
"            JOIN gqlite_labels AS labels ON labels.id = l.label WHERE l.node_id = ";

stream << ( _node_id );

stream << ")\n"
"";

    return stream.str();
  }
  inline std::string get_debug_stats(const std::string& _graph_name)
  {
    std::stringstream stream;
    
stream << "SELECT COUNT(*) FROM gqlite_";

stream << ( _graph_name );

stream << "_nodes\n"
"UNION ALL SELECT COUNT(*) FROM gqlite_";

stream << ( _graph_name );

stream << "_edges\n"
"UNION ALL SELECT COUNT(*) FROM gqlite_";

stream << ( _graph_name );

stream << "_labels\n"
"UNION ALL SELECT COUNT(j.value) FROM gqlite_";

stream << ( _graph_name );

stream << "_nodes t, json_each(properties) j\n"
"UNION ALL SELECT COUNT(j.value) FROM gqlite_";

stream << ( _graph_name );

stream << "_edges t, json_each(properties) j\n"
"UNION ALL SELECT COUNT(*) FROM gqlite_labels\n"
"UNION ALL SELECT COUNT(DISTINCT label) FROM gqlite_";

stream << ( _graph_name );

stream << "_labels";

    return stream.str();
  }
  inline std::string graph_create(const std::string& _graph_name)
  {
    std::stringstream stream;
    
stream << "CREATE TABLE gqlite_";

stream << ( _graph_name );

stream << "_nodes(id INTEGER PRIMARY KEY AUTOINCREMENT, properties TEXT NOT NULL);\n"
"CREATE TABLE gqlite_";

stream << ( _graph_name );

stream << "_edges(id INTEGER PRIMARY KEY AUTOINCREMENT,\n"
"        label INTEGER,\n"
"        properties TEXT NOT NULL,\n"
"        left INTEGER,\n"
"        right INTEGER,\n"
"        FOREIGN KEY(label) REFERENCES gqlite_labels(id),\n"
"        FOREIGN KEY(left) REFERENCES gqlite_";

stream << ( _graph_name );

stream << "_nodes(id), FOREIGN KEY(right) REFERENCES gqlite_";

stream << ( _graph_name );

stream << "_nodes(id));\n"
"CREATE TABLE gqlite_";

stream << ( _graph_name );

stream << "_labels(label INTEGER, node_id INTEGER,\n"
"        FOREIGN KEY(label) REFERENCES gqlite_labels(id), FOREIGN KEY(node_id) REFERENCES gqlite_";

stream << ( _graph_name );

stream << "_nodes(id));\n"
"\n"
"---------------- views ----------------\n"
"\n"
"-- view for querying for undirected edges\n"
"CREATE VIEW gqlite_";

stream << ( _graph_name );

stream << "_edges_undirected (id, label, properties, left, right) AS\n"
"        SELECT id, label, properties, left, right FROM gqlite_";

stream << ( _graph_name );

stream << "_edges\n"
"        UNION SELECT id, label, properties, right, left FROM gqlite_";

stream << ( _graph_name );

stream << "_edges;\n"
"\n"
"-- view for getting the nodes as a json representation\n"
"CREATE VIEW gqlite_";

stream << ( _graph_name );

stream << "_nodes_as_json (id, node) AS\n"
"        SELECT id, json_object('id', id, 'properties', json(properties), \n"
"          'labels', (SELECT json_group_array(value) FROM json_each(labels) AS result WHERE value IS NOT NULL),\n"
"          'type', 'node') FROM\n"
"          (SELECT n.id AS id, n.properties AS properties, json_group_array(labels.label) AS labels\n"
"            FROM gqlite_";

stream << ( _graph_name );

stream << "_nodes n\n"
"            LEFT JOIN gqlite_";

stream << ( _graph_name );

stream << "_labels as l ON l.node_id = n.id\n"
"            LEFT JOIN gqlite_labels AS labels ON labels.id = l.label\n"
"            GROUP BY n.id);\n"
"\n"
"CREATE VIEW gqlite_";

stream << ( _graph_name );

stream << "_edges_as_json (id, edge) AS\n"
"        SELECT e.id, json_object('id', e.id, 'properties', json(e.properties), \n"
"          'label', label.label,\n"
"          'type', 'edge') FROM\n"
"          gqlite_";

stream << ( _graph_name );

stream << "_edges e\n"
"          JOIN gqlite_labels label ON label.id = e.label\n"
"\n"
"";

    return stream.str();
  }
  inline std::string graph_has(const std::string& _graph_name)
  {
    std::stringstream stream;
    
stream << "SELECT count(*) FROM sqlite_master\n"
"  WHERE type='table'\n"
"    AND (name='gqlite_";

stream << ( _graph_name );

stream << "_nodes' or name='gqlite_";

stream << ( _graph_name );

stream << "_edges' or name='gqlite_";

stream << ( _graph_name );

stream << "_labels')";

    return stream.str();
  }
  inline std::string label_create_table()
  {
    std::stringstream stream;
    
stream << "CREATE TABLE gqlite_labels(id INTEGER PRIMARY KEY AUTOINCREMENT, label TEXT NOT NULL UNIQUE);\n"
"INSERT INTO gqlite_labels(id, label) VALUES (0, \"\")";

    return stream.str();
  }
  inline std::string label_get_from_id()
  {
    std::stringstream stream;
    
stream << "SELECT label FROM gqlite_labels WHERE id = ?001\n"
"";

    return stream.str();
  }
  inline std::string label_get_from_name()
  {
    std::stringstream stream;
    
stream << "SELECT id FROM gqlite_labels WHERE label = ?001\n"
"";

    return stream.str();
  }
  inline std::string label_insert()
  {
    std::stringstream stream;
    
stream << "INSERT INTO gqlite_labels(label) VALUES (?001)";

    return stream.str();
  }
  inline std::string node_add_label(const std::string& _graph_name)
  {
    std::stringstream stream;
    
stream << "INSERT INTO gqlite_";

stream << ( _graph_name );

stream << "_labels(label, node_id)  SELECT label.value, node.value FROM json_each(?001) label  JOIN json_each(?002) node";

    return stream.str();
  }
  inline std::string node_add_labels(const std::string& _graph_name, const std::string& _what)
  {
    std::stringstream stream;
    
stream << "INSERT INTO gqlite_";

stream << ( _graph_name );

stream << "_labels(label, node_id) ";

stream << ( _what );

stream << "\n"
"";

    return stream.str();
  }
  inline std::string node_add_properties(const std::string& _graph_name, const std::string& _what, const std::string& _expr, const std::string& _path, const std::string& _node_id)
  {
    std::stringstream stream;
    
stream << "UPDATE gqlite_";

stream << ( _graph_name );

stream << "_nodes AS tblu SET properties=json_patch(tblu.properties, json_set('{}', \"";

stream << ( _path );

stream << "\", gqlite_jsonify(";

stream << ( _expr );

stream << "))) ";

stream << ( _what );

stream << " WHERE tblu.id = ";

stream << ( _node_id );

    return stream.str();
  }
  inline std::string node_create(const std::string& _graph_name, const std::string& _values)
  {
    std::stringstream stream;
    
stream << "INSERT INTO gqlite_";

stream << ( _graph_name );

stream << "_nodes (id, properties) ";

stream << ( _values );

stream << "\n"
"RETURNING id";

    return stream.str();
  }
  inline std::string node_delete(const std::string& _graph_name, const std::string& _what)
  {
    std::stringstream stream;
    
stream << "DELETE FROM gqlite_";

stream << ( _graph_name );

stream << "_labels WHERE node_id IN (";

stream << ( _what );

stream << ");\n"
"DELETE FROM gqlite_";

stream << ( _graph_name );

stream << "_nodes WHERE id IN (";

stream << ( _what );

stream << ")";

    return stream.str();
  }
  inline std::string node_get_labels(const std::string& _graph_name)
  {
    std::stringstream stream;
    
stream << "SELECT label FROM gqlite_";

stream << ( _graph_name );

stream << "_labels WHERE node_id = ?001";

    return stream.str();
  }
  inline std::string node_get_properties(const std::string& _graph_name)
  {
    std::stringstream stream;
    
stream << "SELECT properties FROM gqlite_";

stream << ( _graph_name );

stream << "_nodes WHERE id = ?001";

    return stream.str();
  }
  inline std::string node_remove_label(const std::string& _graph_name, const std::string& _what, const std::string& _labels)
  {
    std::stringstream stream;
    
stream << "DELETE FROM gqlite_";

stream << ( _graph_name );

stream << "_labels WHERE node_id IN (";

stream << ( _what );

stream << ") AND label IN (";

stream << ( _labels );

stream << ")\n"
"";

    return stream.str();
  }
  inline std::string node_remove_property(const std::string& _graph_name, const std::string& _what, const std::string& _path, const std::string& _node_id)
  {
    std::stringstream stream;
    
stream << "UPDATE gqlite_";

stream << ( _graph_name );

stream << "_nodes AS tblu SET properties=json_remove(tblu.properties, \"";

stream << ( _path );

stream << "\") ";

stream << ( _what );

stream << " WHERE tblu.id = ";

stream << ( _node_id );

    return stream.str();
  }
  inline std::string node_set_property(const std::string& _graph_name, const std::string& _what, const std::string& _expr, const std::string& _path, const std::string& _node_id)
  {
    std::stringstream stream;
    
stream << "UPDATE gqlite_";

stream << ( _graph_name );

stream << "_nodes AS tblu SET properties=json_patch('{}', json_set(tblu.properties, \"";

stream << ( _path );

stream << "\", gqlite_jsonify(";

stream << ( _expr );

stream << "))) ";

stream << ( _what );

stream << " WHERE tblu.id = ";

stream << ( _node_id );

    return stream.str();
  }
  inline std::string node_select_many(const std::string& _graph_name, int _idx)
  {
    std::stringstream stream;
    
stream << "SELECT nas.node as node_map, ROW_NUMBER() over () as node_rowid\n"
"  FROM gqlite_";

stream << ( _graph_name );

stream << "_nodes_as_json nas\n"
"  JOIN json_each(?";

stream << ( to_string_fixed_width(_idx, 3) );

stream << ") s ON s.value = nas.id\n"
"";

    return stream.str();
  }
  inline std::string table_has()
  {
    std::stringstream stream;
    
stream << "SELECT count(*) FROM sqlite_master WHERE type='table' AND (name=?001)";

    return stream.str();
  }
  inline std::string uid_create_table()
  {
    std::stringstream stream;
    
stream << "CREATE TABLE gqlite_uids(name TEXT PRIMARY KEY, value INTEGER NOT NULL);\n"
"INSERT INTO gqlite_uids VALUES ('nodes', 0);\n"
"INSERT INTO gqlite_uids VALUES ('edges', 0)\n"
"";

    return stream.str();
  }
  inline std::string uid_next()
  {
    std::stringstream stream;
    
stream << "UPDATE gqlite_uids SET value=value+1 WHERE name=?001\n"
"RETURNING value";

    return stream.str();
  }
}
//END backends/sqlite_queries.h
//BEGIN backends/sqlite.h


namespace gqlite::backends
{
  class sqlite : public backend
  {
  public:
    sqlite(void* _db);
    virtual ~sqlite();
    static sqlite* from_file(const std::string& _filename);
  public:
    value execute_oc_query(oc::algebra::node_csp _node) override;
  private:
    struct data;
    data* const d;
  };
}
//END backends/sqlite.h
//BEGIN backend.cpp


using namespace gqlite;

backend::~backend()
{}
//END backend.cpp
//BEGIN connection.cpp


#include <sstream>





using namespace gqlite;

struct connection::data
{
  ~data()
  {
    delete backend_;
  }
  backend* backend_ = nullptr;
};

connection::connection()
{}

connection::connection(const connection& _rhs) : d(_rhs.d)
{
}

connection::connection(backend* _backend) : d(new data{_backend})
{
}

connection& connection::operator=(const connection& _rhs)
{
  d = _rhs.d;
  return *this;
}

connection::~connection()
{
}

connection connection::create_from_sqlite(void* _sqlite_connection, const gqlite::value&)
{
  return connection(new backends::sqlite(_sqlite_connection));
}

connection connection::create_from_sqlite_file(const std::string& _filename, const gqlite::value&)
{
  return connection(backends::sqlite::from_file(_filename));
}

value connection::execute_oc_query(const std::string& _string, const value_map& _bindings)
{
  std::stringstream ss(_string);
  oc::lexer l(&ss);
  oc::parser parser(&l, _bindings);
  oc::algebra::node_csp node = parser.parse();
  return d->backend_->execute_oc_query(node);
}
//END connection.cpp
//BEGIN exception.cpp
#include <gqlite.h>



using namespace gqlite;

struct exception::data
{
  exception_stage stage;
  exception_code code;
  std::string error;
  mutable std::string full_error;
  const char* c_error;
};

struct gqlite::exception_reader
{
  const exception& ex;
  exception_code get_code() const
  {
    return ex.d->code;
  }
  std::string get_message() const
  {
    return ex.d->error;
  }
};

exception_code gqlite::get_execption_code(const exception& _exception)
{
  return exception_reader{_exception}.get_code();
}

std::string gqlite::get_execption_message(const exception& _exception)
{
  return exception_reader{_exception}.get_message();
}


struct gqlite::exception_maker
{
  exception operator()(exception_stage _stage, exception_code _code, const std::string& _error)
  {
    return exception(new exception::data{_stage, _code, _error, std::string(), nullptr});
  }
  exception operator()(exception_stage _stage, exception_code _code, const char* _error)
  {
    return exception(new exception::data{_stage, _code, std::string(), std::string(), _error});
  }
  exception operator()(exception_stage _stage, exception _ex)
  {
    _ex.d->stage = _stage;
    return _ex;
  }
};

[[noreturn]] void gqlite::throw_exception(exception_stage _stage, exception_code _code, const std::string& _error)
{
  throw exception_maker()(_stage, _code, _error);
}

[[noreturn]] void gqlite::throw_exception(exception_stage _stage, exception_code _code, const char* _error)
{
  throw exception_maker()(_stage, _code, _error);
}

[[noreturn]] void gqlite::rethrow_exception(exception_stage _stage, const exception& _ex)
{
  throw exception_maker()(_stage, _ex);
}

exception::exception(data* _d) : d(_d)
{
}

exception::~exception()
{
  delete d;
}

exception::exception(exception&& _rhs) : d(_rhs.d)
{
  *(const_cast<data**>(&_rhs.d)) = nullptr;
}

exception::exception(const exception& _rhs) : d(new data(*_rhs.d))
{
}

const char* exception::what() const throw()
{
  if(d->full_error.empty())
  {
    switch(d->stage)
    {
      using enum exception_stage;
      case unspecified:
        break;
      case runtime:
        d->full_error = "RunTime: ";
        break;
      case compiletime:
        d->full_error = "CompileTime: ";
        break;
    }
    switch(d->code)
    {
      using enum exception_code;
      case unspecified:
        break;
      case internal_error:
        d->full_error += "InternalError: "; break;
      case invalid_json:
        d->full_error += "InvalidJson: "; break;
      case table_error:
        d->full_error += "TableError: "; break;
      case value_error:
        d->full_error += "ValueError: "; break;
      case unimplemented_error:
        d->full_error += "UnimplementedError: "; break;
      case column_name_conflict:
        d->full_error += "ColumnNameConflict: "; break;
      case integer_overflow:
        d->full_error += "IntegerOverflow: "; break;
      case invalid_argument_type:
        d->full_error += "InvalidArgumentType: "; break;
      case invalid_delete:
        d->full_error += "InvalidDelete: "; break;
      case invalid_number_literal:
        d->full_error += "InvalidNumberLiteral: "; break;
      case invalid_number_of_arguments:
        d->full_error += "InvalidNumberOfArguments: "; break;
      case invalid_parameter_use:
        d->full_error += "InvalidParameterUse: "; break;
      case negative_integer_argument:
        d->full_error += "NegativeIntegerArgument: "; break;
      case non_constant_expression:
        d->full_error += "NonConstantExpression: "; break;
      case parse_error:
        d->full_error += "ParseError: "; break;
      case requires_directed_relationship:
        d->full_error += "RequiresDirectedRelationship: "; break;
      case undefined_variable:
        d->full_error += "UndefinedVariable: "; break;
      case unknown_function:
        d->full_error += "UnknownFunction: "; break;
      case unexpected_syntax:
        d->full_error += "UnexpectedSyntax: "; break;
      case variable_already_bound:
        d->full_error += "VariableAlreadyBound: "; break;
      case variable_type_conflict:
        d->full_error += "VariableTypeConflict: "; break;
    }
    if(d->c_error)
    {
      d->full_error += d->c_error;
    } else {
      d->full_error += d->error;
    }
  }
  return d->full_error.c_str();
}

exception& exception::operator=(const exception& _rhs)
{
  *d = *_rhs.d;
  return *this;
}
//END exception.cpp
//BEGIN gqlite-c.cpp






#define BEGIN_CHECK                       \
  if(not _context->error_message.empty()) \
  {                                       \
    gqlite_error("Error message '{}' was not cleared from context!", _context->error_message);  \
  }                                       \
  try                                     \
  {
#define END_CHECK                                 \
  } catch(const gqlite::exception& _ex)           \
  {                                               \
    _context->error_message = _ex.what();         \
    if(_context->error_message.empty())           \
    {                                             \
      _context->error_message = "Unknown error";  \
    }                                             \
  }

extern "C"
{

  struct gqlite_api_context
  {
    std::string error_message;
    ~gqlite_api_context()
    {
      release_memory();
    }
    char* str = nullptr;
    char* set_string(const std::string& _string)
    {
      release_memory();
      str = new char[_string.size() + 1];
      std::copy(_string.begin(), _string.end() + 1, str);
      return str;
    }
    void release_memory()
    {
      delete[] str;
      str = nullptr;
    }
  };

  gqlite_api_context_t gqlite_api_context_create()
  {
    return new gqlite_api_context;
  }
  void gqlite_api_context_destroy(gqlite_api_context_t _context)
  {
    delete _context;
  }
  const char* gqlite_api_context_get_message(gqlite_api_context_t _context)
  {
    return _context->error_message.c_str();
  }
  bool gqlite_api_context_has_error(gqlite_api_context_t _context)
  {
    return not _context->error_message.empty();
  }
  void gqlite_api_context_clear_error(gqlite_api_context_t _context)
  {
    _context->error_message.clear();
  }

  struct gqlite_connection
  {
    gqlite::connection db;
  };
  struct gqlite_value
  {
    gqlite::value value;
  };

  gqlite_connection_t gqlite_connection_create_from_sqlite(gqlite_api_context_t _context, void* _handle, gqlite_value_t _options)
  {
    BEGIN_CHECK
    gqlite::value options;
    if(_options) options = _options->value;
    return new gqlite_connection{gqlite::connection::create_from_sqlite(_handle, options)};
    END_CHECK
    return nullptr;
  }
  gqlite_connection_t gqlite_connection_create_from_sqlite_file(gqlite_api_context_t _context, const char* _filename, gqlite_value_t _options)
  {
    BEGIN_CHECK
    gqlite::value options;
    if(_options) options = _options->value;
    return new gqlite_connection{gqlite::connection::create_from_sqlite_file(_filename, options)};
    END_CHECK
    return nullptr;
  }

  void gqlite_connection_destroy(gqlite_api_context_t _context, gqlite_connection_t _connection)
  {
    BEGIN_CHECK
    delete _connection;
    END_CHECK
  }

  gqlite_value_t gqlite_connection_oc_query(gqlite_api_context_t _context, gqlite_connection_t _connection, const char* _query, gqlite_value_t _bindings)
  {
    BEGIN_CHECK
    gqlite::value_map bindings;
    if(_bindings)
    {
      bindings = _bindings->value.to_map();
    }
    return new gqlite_value{ _connection->db.execute_oc_query(_query, bindings) };
    END_CHECK
    return nullptr;
  }

  gqlite_value_t gqlite_value_create(gqlite_api_context_t)
  {
    return new gqlite_value;
  }
  void gqlite_value_destroy(gqlite_api_context_t, gqlite_value_t _value)
  {
    delete _value;
  }
  const char* gqlite_value_to_json(gqlite_api_context_t _context, gqlite_value_t _value)
  {
    BEGIN_CHECK
    return _context->set_string(_value->value.to_json());
    END_CHECK
    return nullptr;
  }
  gqlite_value_t gqlite_value_from_json(gqlite_api_context_t _context, const char* _json)
  {
    BEGIN_CHECK
    return new gqlite_value{gqlite::value::from_json(_json)};
    END_CHECK
    return nullptr;
  }

  bool gqlite_value_is_valid(gqlite_api_context_t, gqlite_value_t _value)
  {
    return _value and _value->value.get_type() != gqlite::value_type::invalid;
  }

}
//END gqlite-c.cpp
//BEGIN value.cpp


#include <sstream>
#include <variant>




using namespace gqlite;

const char* gqlite::to_string(value_type _type)
{
  switch(_type)
  {
    using enum value_type;
    case invalid: return "invalid";
    case boolean: return "boolean";
    case integer: return "integer";
    case floating_point: return "number";
    case string: return "string";
    case map: return "map";
    case vector: return "vector";
  }
  return "unknown value type";
}

std::string gqlite::to_string(const value& _value)
{
  return _value.to_json();
}
struct value::data
{
  struct invalid
  {
    bool operator==(const invalid&) const { return true; }
  };
  value_type type;
  std::variant<invalid, bool, int64_t, double, std::string, value_map, value_vector> value_container;

  void to_json(std::stringstream& _stream);
};

namespace {
  /**
   * @internal
   * class use to read json files
   */
  struct json_reader
  {
    std::stringstream stream;
    int last_char;
    /// @brief get the next char
    void fetch_next_non_space_char()
    {
      while(std::isspace(fetch_next_char()))
      {
        if(last_char == std::stringstream::traits_type::eof())
        {
          return;
        } else if(stream.fail())
        {
          throw_exception(exception_stage::unspecified, exception_code::invalid_json, "Failure in getting json data from stream");
        }
      }
    };
    int fetch_next_char()
    {
      return (last_char = stream.get());
    }
    /// @brief start parsing
    /// @throw gqlite::exception
    /// @return the parsed value
    gqlite::value start()
    {
      fetch_next_non_space_char();
      return read_value();
    }
    /// @brief read a string
    /// @return the string without quotation
    std::string read_string()
    {
      is_char('"');
      fetch_next_char();
      std::string string;
      bool keep_nc = false; // this is used to indicate if the last character was a '\' or not
      while(keep_nc or last_char != '"')
      {
        if(not keep_nc and last_char == '\\')
        {
          keep_nc = true; // make sure to ignore the next character if it is a '"' or to properly handle a '\n'
        } else {
          if(keep_nc)
          {
            keep_nc = false;
            if(last_char == 'n')
            {
              string += '\n';
            } else {
              string += char(last_char);
            }
          } else {
            string += char(last_char);
          }
        }
        errors::check_condition(stream.good(), exception_stage::unspecified, exception_code::invalid_json, "Unfinished string: {}.", string);
        fetch_next_char();
      }
      fetch_next_char(); // eat the '"'
      return string;
    }
    /// @brief throw an exception if last_char different from @param _c 
    void is_char(int _c)
    {
      errors::check_condition(last_char == _c, exception_stage::unspecified, exception_code::invalid_json, "Expected '{}' but got '{}'.", char(_c), char(last_char));
    }
    /// @brief read the next value
    /// @return and return it
    gqlite::value read_value()
    {
      switch (last_char)
      {
      case '{':
      {
        // Parse object
        value_map map;
        fetch_next_non_space_char();
        while(last_char != '}')
        {
          std::string str = read_string();
          is_char(':');
          fetch_next_non_space_char();
          gqlite::value val = read_value();
          map[str] = val;
          if(last_char == ',')
          {
            fetch_next_non_space_char();
          } else {
            break;
          }
        }
        is_char('}');
        fetch_next_non_space_char();
        return map;
      }
      case '[':
      {
        // Parse array
        value_vector vec;
        fetch_next_non_space_char();
        while(last_char != ']')
        {
          gqlite::value val = read_value();
          vec.push_back(val);
          if(last_char == ',')
          {
            fetch_next_non_space_char();
          } else {
            break;
          }
        }
        is_char(']');
        fetch_next_non_space_char();
        return vec;
      }
      case '"':
        return read_string();
      case 't':
      {
        // Parse true
        fetch_next_non_space_char(); is_char('r');
        fetch_next_non_space_char(); is_char('u');
        fetch_next_non_space_char(); is_char('e');
        fetch_next_non_space_char();
        return true;
      }
      case 'f':
      {
        // Parse false
        fetch_next_non_space_char(); is_char('a');
        fetch_next_non_space_char(); is_char('l');
        fetch_next_non_space_char(); is_char('s');
        fetch_next_non_space_char(); is_char('e');
        fetch_next_non_space_char();
        return false;
      }
      case 'n':
      {
        // Parse null
        fetch_next_non_space_char(); is_char('u');
        fetch_next_non_space_char(); is_char('l');
        fetch_next_non_space_char(); is_char('l');
        fetch_next_non_space_char();
        return value();
      }
      // Parse number
      case '-':
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
      {
        std::string number;
        number += char(last_char);
        fetch_next_non_space_char();
        bool is_integer = true;
        while((last_char >= '0' and last_char <= '9') or last_char == '.' or last_char == 'e' or last_char == '+' or last_char == '-')
        {
          is_integer = is_integer and (last_char != '.' and last_char != 'e');
          number += char(last_char);
          fetch_next_non_space_char();
        }
        if(is_integer)
        {
          return int64_t(std::stoll(number));
        } else {
          return std::stod(number);
        }
      }
      default:
        throw_exception(exception_stage::unspecified, exception_code::invalid_json, "Unexpected '{}'.", char(last_char));
      }
    }
  };
}

void value::data::to_json(std::stringstream& _stream)
{
  bool first = true;
  switch(type)
  {
  case value_type::boolean:
    _stream << (std::get<bool>(value_container) ? "true" : "false");
    break;
  case value_type::integer:
    _stream << std::get<int64_t>(value_container);
    break;
  case value_type::floating_point:
    _stream << std::get<double>(value_container);
    break;
  case value_type::string:
  {
    std::string string = std::get<std::string>(value_container);
    std::size_t start_pos = 0;
    while((start_pos = string.find("\"", start_pos)) != std::string::npos)
    {
      string.replace(start_pos, 1, "\\\"");
      start_pos += 2;
    }
    _stream << '"' << string << '"';
  }
    break;
  case value_type::map:
    _stream << '{';
    for(auto const& [k, v] : std::get<value_map>(value_container))
    {
      if(first)
      {
        first = false;
      } else {
        _stream << ',';
      }
      _stream << '"' << k << "\":";
      v.d->to_json(_stream);
    }
    _stream << '}';
    break;
  case value_type::vector:
    _stream << '[';
    for(const value& v : std::get<value_vector>(value_container))
    {
      if(first)
      {
        first = false;
      } else {
        _stream << ',';
      }
      v.d->to_json(_stream);
    }
    _stream << ']';
    break;
  case value_type::invalid:
    _stream << "null";
  }
}

value::value() : d(new data{value_type::invalid, data::invalid{}})
{}

value::value(const value& _rhs) : d(_rhs.d)
{
}

value& value::operator=(const value& _rhs)
{
  d = _rhs.d;
  return *this;
}

value::~value()
{}

value::value(bool _v) : d(new data{value_type::boolean, _v})
{}

value::value(int64_t _v) : d(new data{value_type::integer, _v})
{}

value::value(double _v) : d(new data{value_type::floating_point, _v})
{}

value::value(const char* _v) : value(std::string(_v))
{}

value::value(const std::string& _v) : d(new data{value_type::string, _v})
{}

value::value(const value_map& _v) : d(new data{value_type::map, _v})
{}

value::value(const value_vector& _v) : d(new data{value_type::vector, _v})
{}

value_type value::get_type() const
{
  return d->type;
}

bool value::operator==(const value& _rhs) const
{
  return d->type == _rhs.d->type and d->value_container == _rhs.d->value_container;
}

bool value::operator<(const value& _rhs) const
{
  errors::check_condition(d->type == _rhs.d->type, exception_stage::unspecified, exception_code::value_error, "Value of different types are not comparable with '<'");
  switch(d->type)
  {
  case value_type::boolean:
    return to_bool() < _rhs.to_bool();
  case value_type::integer:
    return to_integer() < _rhs.to_integer();
  case value_type::floating_point:
    return to_double() < _rhs.to_double();
  case value_type::string:
    return to_string() < _rhs.to_string();
  case value_type::vector:
    return to_vector() < _rhs.to_vector();
  default:
    throw_exception(exception_stage::unspecified, exception_code::value_error, "Cannot compare {} and {} with inferior operator.", *this, _rhs);
  }
}

bool value::to_bool() const
{
  switch(d->type)
  {
  case value_type::boolean:
    return std::get<bool>(d->value_container);
  case value_type::integer:
    return std::get<int64_t>(d->value_container) != 0;
  case value_type::floating_point:
    return std::get<double>(d->value_container) != 0.0;
  default:
    throw_exception(exception_stage::unspecified, exception_code::value_error, "Value is not a bool, it is {}.", d->type);
  }
}

int64_t value::to_integer() const
{
  switch(d->type)
  {
    case value_type::integer:
      return std::get<int64_t>(d->value_container);
    case value_type::floating_point:
      return std::get<double>(d->value_container);
    default:
      throw_exception(exception_stage::unspecified, exception_code::value_error, "Value is not a number, it is '{}'.", d->type);
  }
}

double value::to_double() const
{
  switch(d->type)
  {
    case value_type::integer:
      return std::get<int64_t>(d->value_container);
    case value_type::floating_point:
      return std::get<double>(d->value_container);
    default:
      throw_exception(exception_stage::unspecified, exception_code::value_error, "Value is not a number, it is '{}'.", d->type);
  }
}

std::string value::to_string() const
{
  errors::check_condition(d->type == value_type::string, exception_stage::unspecified, exception_code::value_error, "Value is not a string, it is '{}'.", d->type);
  return std::get<std::string>(d->value_container);
}

value_map value::to_map() const
{
  errors::check_condition(d->type == value_type::map, exception_stage::unspecified, exception_code::value_error, "Value is not a map, it is '{}'.", d->type);
  return std::get<value_map>(d->value_container);
}

value_vector value::to_vector() const
{
  errors::check_condition(d->type == value_type::vector, exception_stage::unspecified, exception_code::value_error, "Value is not a vector, it is '{}'.", d->type);
  return std::get<value_vector>(d->value_container);
}

std::string value::to_json() const
{
  std::stringstream ss;
  ss.precision(15);
  d->to_json(ss);
  return ss.str();
}

value value::from_json(const std::string& _json)
{
  json_reader reader{std::stringstream(_json), 0};
  return reader.start();
}
//END value.cpp
//BEGIN oc/lexer.cpp


#include <algorithm>
#include <iostream>
#include <vector>




using namespace gqlite::oc;

struct lexer::data {
  std::istream* stream;
  int lastChar;
  int col;
  int line;
  int followingnewline;
  std::vector<token> ungotten;
};

lexer::lexer(std::istream* sstream) : d(new data)
{
  d->col = 1;
  d->line = 1;
  d->followingnewline = 1;
  d->stream = sstream;
}

lexer::~lexer()
{
  delete d;
}

#define IDENTIFIER_IS_KEYWORD( tokenname, tokenid) \
  if( identifierStr == tokenname) \
  { \
    return token(token_type::tokenid, line(), initial_col); \
  }

#define CHAR_IS_TOKEN( tokenchar, tokenid ) \
  if( lastChar == tokenchar ) \
  { \
    return token(token_type::tokenid, line(), initial_col); \
  }

#define CHAR_IS_TOKEN_OR_TOKEN( tokenchar, tokendecidechar, tokenid_1, tokenid_2 ) \
  if( lastChar == tokenchar  ) \
  { \
    if( get_next_char() == tokendecidechar ) \
    { \
      return token(token_type::tokenid_2, line(), initial_col); \
    } else { \
      unget(); \
      return token(token_type::tokenid_1, line(), initial_col); \
    } \
  }

#define CHAR_IS_TOKEN_OR_TOKEN_OR_TOKEN( tokenchar_1, tokenchar_2, tokenchar_3, tokenid_1, tokenid_2, tokenid_3 ) \
  if( lastChar == tokenchar_1 ) \
  { \
    int nextChar = get_next_char(); \
    if( nextChar == tokenchar_2 ) \
    { \
      return token(token_type::tokenid_2, line(), initial_col); \
    } else if( nextChar  == tokenchar_3 ) \
    { \
      return token(token_type::tokenid_3, line(), initial_col); \
    } else { \
      unget(); \
      return token(token_type::tokenid_1, line(), initial_col); \
    } \
  }

#define CHAR_IS_TOKEN_OR_TOKEN_OR_TOKEN_OR_TOKEN( tokenchar_1, tokenchar_2, tokenchar_3, tokenchar_4, tokenid_1, tokenid_2, tokenid_3, tokenid_4 ) \
  if( lastChar == tokenchar_1 ) \
  { \
    int nextChar = get_next_char(); \
    if( nextChar == tokenchar_2 ) \
    { \
      return token(token_type::tokenid_2, line(), initial_col); \
    } else if( nextChar  == tokenchar_3 ) \
    { \
      return token(token_type::tokenid_3, line(), initial_col); \
    } else if( nextChar  == tokenchar_4 ) \
    { \
      return token(token_type::tokenid_4, line(), initial_col); \
    } else { \
      unget(); \
      return token(token_type::tokenid_1, line(), initial_col); \
    } \
  }

void lexer::unget(const token& _token)
{
  d->ungotten.push_back(_token);
}

token lexer::next_token(int _flags)
{
  if(not d->ungotten.empty())
  {
    token t = d->ungotten.back();
    d->ungotten.pop_back();
    return t;
  }
  int lastChar = get_next_non_separator_char();
  int initial_line = line() - 1;
  int initial_col = column() - 1;
  if( eof() ) return token(token_type::END_OF_FILE, line(), initial_col);
  std::string identifierStr;
  // Test for comment
  token commenttoken;
  if( ignore_comment( commenttoken, lastChar ) )
  {
    return commenttoken;
  }
  // if it is alpha, it's an identifier or a keyword
  if(lastChar == '$')
  {
    identifierStr = get_identifier(lastChar);
    return token(token_type::PARAMETER, identifierStr, line(), initial_col);
  } else if(std::isalpha(lastChar) or lastChar == '_')
  {
    identifierStr = get_identifier(lastChar);
    if((_flags & mode::disable_keywords) != mode::disable_keywords)
    {
#define TOKEN_KEYWORD(_K_) IDENTIFIER_IS_KEYWORD(# _K_, _K_)
#define TOKEN_KEYWORD2(_K_, _S_) IDENTIFIER_IS_KEYWORD(_S_, _K_)
      TOKEN_KEYWORD(AND)
TOKEN_KEYWORD(AS)
TOKEN_KEYWORD(ASC)
TOKEN_KEYWORD(BY)
TOKEN_KEYWORD(CALL)
TOKEN_KEYWORD(CREATE)
TOKEN_KEYWORD(DELETE)
TOKEN_KEYWORD(DESC)
TOKEN_KEYWORD(DETACH)
TOKEN_KEYWORD(FALSE)
TOKEN_KEYWORD(IN)
TOKEN_KEYWORD(IS)
TOKEN_KEYWORD(LIMIT)
TOKEN_KEYWORD(NOT)
TOKEN_KEYWORD(MATCH)
TOKEN_KEYWORD2(NULL_CAPS, "NULL")
TOKEN_KEYWORD2(NULL_TOKEN, "null")
TOKEN_KEYWORD(OR)
TOKEN_KEYWORD(ORDER)
TOKEN_KEYWORD(REMOVE)
TOKEN_KEYWORD(RETURN)
TOKEN_KEYWORD(SET)
TOKEN_KEYWORD(SKIP)
TOKEN_KEYWORD(TRUE)
TOKEN_KEYWORD(UNWIND)
TOKEN_KEYWORD(XOR)
TOKEN_KEYWORD(YIELD)
TOKEN_KEYWORD(WITH)
TOKEN_KEYWORD(WHERE)

#undef TOKEN_KEYWORD
#undef TOKEN_KEYWORD2
      IDENTIFIER_IS_KEYWORD("ASCENDING", ASC)
      IDENTIFIER_IS_KEYWORD("DESCENDING", DESC)
      IDENTIFIER_IS_KEYWORD("true", TRUE)
      IDENTIFIER_IS_KEYWORD("false", FALSE)
    }
    return token(token_type::IDENTIFIER, identifierStr, line(), initial_col);
  } else if(lastChar == '`') {
    gqlite::oc::token tok = get_string(lastChar);
    tok.type = token_type::IDENTIFIER;
    return tok;
  } else if(lastChar == '"' or lastChar == '\'' ) {
    return get_string(lastChar);
  } else if(lastChar >= '0' and lastChar <= '9') {
    std::string number;
    number += char(lastChar);
    lastChar = get_next_char();

    bool is_integer = true;
    if(lastChar == 'o' or lastChar == 'x')
    {
      number += char(lastChar);
      lastChar = get_next_char();
      // Octal or hexa decimal integer
      while((lastChar >= '0' and lastChar <= '9')
        or (lastChar >= 'A' and lastChar <= 'F')
        or (lastChar >= 'a' and lastChar <= 'f'))
      {
        number += char(lastChar);
        lastChar = get_next_char();
      }
      unget();
    } else {

      while((lastChar >= '0' and lastChar <= '9')
        or (lastChar == '.' and is_integer)
        or lastChar == 'e' or lastChar == '+' or lastChar == '-')
      {
        is_integer = is_integer and (lastChar != '.' and lastChar != 'e');
        number += char(lastChar);
        lastChar = get_next_char();
      }
      unget();
      if(*std::prev(number.end()) == '.')
      {
        unget();
        is_integer = true;
        number = number.substr(0, number.size() - 1);
      }
    }
    if(is_integer)
    {
      return token(token_type::INTEGER, number, line(), initial_col);
    } else {
      return token(token_type::FLOATING_POINT, number, line(), initial_col);
    }
  } else {
    CHAR_IS_TOKEN(';', SEMI );
    CHAR_IS_TOKEN_OR_TOKEN('.', '.', DOT, DOTDOT);
    CHAR_IS_TOKEN( '{', STARTBRACE );
    CHAR_IS_TOKEN( '}', ENDBRACE );
    CHAR_IS_TOKEN( '(', STARTBRACKET );
    CHAR_IS_TOKEN( ')', ENDBRACKET );
    CHAR_IS_TOKEN( '[', STARTBOXBRACKET );
    CHAR_IS_TOKEN( ']', ENDBOXBRACKET );
    CHAR_IS_TOKEN( '|', PIPE );
    CHAR_IS_TOKEN( ',', COMMA );
    CHAR_IS_TOKEN( '=', EQUAL );
    CHAR_IS_TOKEN_OR_TOKEN( '+', '=', PLUS, PLUS_EQUAL );
    CHAR_IS_TOKEN( '%', PERCENT );
    CHAR_IS_TOKEN( '*', STAR );
    CHAR_IS_TOKEN( '/', DIVIDE );
    CHAR_IS_TOKEN_OR_TOKEN( '!', '=', EXCLAMATION, DIFFERENT)
    CHAR_IS_TOKEN_OR_TOKEN( ':', ':', COLON, COLONCOLON);
    CHAR_IS_TOKEN_OR_TOKEN( '-', '>', MINUS, RIGHT_ARROW);
    CHAR_IS_TOKEN_OR_TOKEN( '>', '=', SUPERIOR, SUPERIOR_EQUAL);
    CHAR_IS_TOKEN_OR_TOKEN_OR_TOKEN_OR_TOKEN( '<', '-', '=', '>', INFERIOR, LEFT_ARROW, INFERIOR_EQUAL, DIFFERENT);
  }
  if( lastChar > 128 ) return next_token();
  identifierStr = lastChar;
  gqlite_error("Unknown token: {} '{}' at {},{} eof status is {} ", lastChar, identifierStr, initial_line, initial_col, eof());
  return token(token_type::UNKNOWN, initial_line, initial_col);
}

int lexer::get_next_non_separator_char()
{
  int lastChar = ' ';
  while( not eof() and isspace(lastChar = get_next_char() )  )
  { // Ignore space
  }
  return lastChar;
}

int lexer::get_next_char()
{
  d->lastChar = d->stream->get();
  if( d->lastChar == '\n' )
  {
    ++d->line;
    ++d->followingnewline;
    d->col = 1;
  } else {
    ++d->col;
    d->followingnewline = 0;
  }
  return d->lastChar;
}

void lexer::unget()
{
  --d->col;
  d->stream->unget();
  if( d->followingnewline > 0 )
  {
    --d->followingnewline;
    --d->line;
  }
}

bool lexer::eof() const
{
  return !d->stream->good();
}

int lexer::line() const
{
  return d->line;
}
int lexer::column() const
{
  return d->col;
}

bool lexer::ignore_comment(token& _token, int _lastChar )
{
  if( _lastChar == '/' )
  {
    int nextChar = get_next_char();
    if( nextChar == '/' )
    { // Mono line comment
      while( not eof() and get_next_char() != '\n' )
      {
      }
      _token = next_token();
      return true;
    } else {
      unget();
    }
  }
  return false;
}

std::string lexer::get_identifier(int lastChar)
{
  std::string identifierStr;
  if( lastChar != 0) {
    identifierStr = lastChar;
  }
  while (not eof() )
  {
    lastChar = get_next_char();
    if( std::isalnum(lastChar) or lastChar == '_')
    {
      identifierStr += lastChar;
    } else {
      unget();
      break;
    }
  }
  return identifierStr;
}

token lexer::get_string(int lastChar)
{
  int initial_col = column();
  std::string identifierStr = "";
  while( not eof() )
  {
    int nextChar = get_next_char();
    if(nextChar == '\\')
    {
      nextChar = get_next_char();
      identifierStr += nextChar;
    } else if( nextChar == lastChar)
    {
      return token(token_type::STRING, identifierStr, line(), initial_col );
    } else {
      identifierStr += nextChar;
    }
  }
  return token(token_type::UNFINISHED_STRING, line(), initial_col);
}
//END oc/lexer.cpp
//BEGIN oc/parser.cpp












using namespace gqlite::oc;

struct parser::data
{
  lexer* lex;
  value_map bindings;
  token tok;
  struct variable_info
  {
    algebra::node_csp node;
    algebra::expression_type type;
  };
  std::unordered_map<std::string, variable_info> bounded_variables;
  algebra::visitors::expression_analyser expression_analyser;
  int id = 0;
  algebra::node_csp parse_call();
  algebra::node_csp parse_create();
  algebra::node_csp parse_match();
  algebra::node_csp parse_return();
  algebra::node_csp parse_with();
  algebra::node_csp parse_unwind();
  algebra::node_csp parse_delete();
  algebra::node_csp parse_set();
  algebra::node_csp parse_remove();
  algebra::modifiers_csp parse_modifiers();
  std::vector<algebra::alternative<algebra::graph_node, algebra::graph_edge>> parse_patterns(bool _creation_mode);
  algebra::node_csp parse_map();
  algebra::node_csp parse_properties();
  algebra::node_csp parse_expression();
  algebra::node_csp parse_unary_expression();
  algebra::node_csp parse_multiplicative_expression();
  algebra::node_csp parse_additive_expression();
  algebra::node_csp parse_relational_expression();
  algebra::node_csp parse_conditional_xor_expression();
  algebra::node_csp parse_conditional_or_expression();
  algebra::node_csp parse_conditional_and_expression();
  algebra::node_csp parse_indexed_access_expression();
  algebra::node_csp parse_member_expression();
  algebra::node_csp parse_terminal_expression();
  algebra::node_csp parse_expression_list();
  std::vector<std::string> parse_path();
  std::vector<algebra::named_expression_csp> parse_named_expressions();
  void validate(algebra::graph_node_csp);
  void validate(algebra::graph_edge_csp);
  std::string generate_anonymous_variable();
  void get_next_token(int _flags = lexer::mode::normal);
  template<typename... _T_>
  [[noreturn]] void report_error(const token& _token, exception_code _code, const std::string& _errorMsg, const _T_&... _values);
  [[noreturn]] void report_unexpected(const token& _token);
  bool is_of_type(const token& _token, token_type _type);
  bool is_of_type(token_type _type);
  int64_t string_to_integer(std::string _string);

  void check_variable_existence(const std::string& _name);
  void bind_variable(const std::string& _name, algebra::node_csp _node, algebra::expression_type _expression_type, bool _allow_rebinding);
};

namespace gqlite
{
  template<>
  inline std::string to_string<token_type>(const token_type& _v)
  {
    return token_type_to_string(_v);
  }
}

void parser::data::check_variable_existence(const std::string& _name)
{
  if(bounded_variables.find(_name) != bounded_variables.end())
  {
    report_error(tok, exception_code::variable_already_bound, "Variable {} is already bound.", _name);
  }
}

void parser::data::bind_variable(const std::string& _name, algebra::node_csp _node, algebra::expression_type _expression_type, bool _allow_rebinding)
{
  if(not _allow_rebinding) check_variable_existence(_name);
  bounded_variables[_name] = {_node, _expression_type };
}

void parser::data::validate(algebra::graph_node_csp _node)
{
  if(_node->get_variable().empty()) return;
  auto it = bounded_variables.find(_node->get_variable());
  if(it == bounded_variables.end())
  {
    bind_variable(_node->get_variable(), _node, algebra::expression_type::node, false);
    return;
  }
  errors::check_variable_type(it->second.type, algebra::expression_type::node, exception_stage::compiletime, _node->get_variable());
  if(_node->get_labels().empty() and not _node->get_properties()) return;
  if(_node->equals(it->second.node)) return;
  report_error(tok, exception_code::variable_already_bound, "Variable {} is already bound.", _node->get_variable());
}

void parser::data::validate(algebra::graph_edge_csp _node)
{
  validate(_node->get_source());
  validate(_node->get_destination());
  if(_node->get_variable().empty()) return;
  auto it = bounded_variables.find(_node->get_variable());
  if(it == bounded_variables.end())
  {
    bind_variable(_node->get_variable(), _node, algebra::expression_type::edge, false);
    return;
  }
  errors::check_variable_type(it->second.type, algebra::expression_type::edge, exception_stage::compiletime, _node->get_variable());
  if(_node->get_labels().empty() and not _node->get_properties()) return;
  if(_node->equals(it->second.node)) return;
  report_error(tok, exception_code::variable_already_bound, "Variable {} is already bound.", _node->get_variable());
}

std::string parser::data::generate_anonymous_variable()
{
  return "__gqlite_anon_" + std::to_string(id++);
}

int64_t parser::data::string_to_integer(std::string _string)
{
  if(_string == "0x" or _string == "-0x" or _string == "0o" or _string == "-0o")
  {
    throw_exception(exception_stage::compiletime, exception_code::invalid_number_literal, "'{}' is not a valid string literal", _string);
  }
  try
  {
    int base = 10;
    if(_string.size() > 2 and (_string[1] == 'x' or (_string[0] == '-' and _string[2] == 'x')))
    {
      base = 16;
    } else if(_string.size() > 2 and (_string[1] == 'o' or (_string[0] == '-' and _string[2] == 'o')))
    {
      base = 8;
      if(_string[0] == '-') _string = '-' + _string.substr(3);
      else _string = _string.substr(2);
    }
    long long i = std::stoll(_string, 0, base);
    if(i > std::numeric_limits<int64_t>::max() or i < std::numeric_limits<int64_t>::min())
    {
      throw_exception(exception_stage::compiletime, exception_code::integer_overflow, "'{}' is too large.", _string);
    }
    return i;
  } catch(const std::out_of_range&)
  {
    throw_exception(exception_stage::compiletime, exception_code::integer_overflow, "'{}' is too large.", _string);
  }
}

algebra::node_csp parser::data::parse_call()
{
  get_next_token();
  std::string name;
  while(tok.type != token_type::END_OF_FILE)
  {
    is_of_type(token_type::IDENTIFIER);
    name += tok.string;
    get_next_token();
    if(tok.type == token_type::DOT)
    {
      name += ".";
      get_next_token();
    } else {
      break;
    }
  }
  std::vector<algebra::node_csp> arguments;
  is_of_type(token_type::STARTBRACKET);
  get_next_token();
  while(tok.type != token_type::ENDBRACKET)
  {
    arguments.push_back(parse_expression());
    if(tok.type == token_type::COMMA)
    {
      get_next_token();
    } else {
      break;
    }
  }
  is_of_type(token_type::ENDBRACKET);
  get_next_token();
  std::vector<std::string> yields;
  if(tok.type == token_type::YIELD)
  {
    get_next_token();
    while(tok.type != token_type::END_OF_FILE)
    {
      is_of_type(token_type::IDENTIFIER);
      yields.push_back(tok.string);
      if(tok.type == token_type::COMMA)
      {
        get_next_token();
      } else {
        break;
      }
    }
  }
  return std::make_shared<algebra::call>(name, arguments, yields);
}

algebra::node_csp parser::data::parse_create()
{
  get_next_token();
  return std::make_shared<algebra::create>(parse_patterns(true));
}

algebra::node_csp parser::data::parse_match()
{
  get_next_token();
  std::vector<algebra::alternative<algebra::graph_node, algebra::graph_edge>> patterns = parse_patterns(false);
  algebra::node_csp where;
  if(tok.type == token_type::WHERE)
  {
    get_next_token();
    where = parse_expression();
  }
  return std::make_shared<algebra::match>(patterns, where);
}

std::vector<algebra::named_expression_csp> parser::data::parse_named_expressions()
{
  std::vector<algebra::named_expression_csp> expressions;
  std::vector<std::string> labels;
  do
  {
    std::string name;
    if(tok.type == token_type::IDENTIFIER)
    {
      name = tok.string;
    }
    algebra::node_csp node = parse_expression();
    if(tok.type == token_type::AS)
    {
      get_next_token();
      is_of_type(token_type::IDENTIFIER);
      name = tok.string;
      get_next_token();
    } else if(node->get_type() != algebra::node_type::variable)
    {
      name = algebra::visitors::stringify().start(node);
    }
    if(std::find(labels.begin(), labels.end(), name) != labels.end())
    {
      report_error(tok, exception_code::column_name_conflict, "Duplicate column name {} is not allowed", name);
    }
    labels.push_back(name);
    expressions.push_back(std::make_shared<algebra::named_expression>(name, node));
    if(tok.type == token_type::COMMA)
    {
      get_next_token();
    } else {
      break;
    }
  } while(true);
  return expressions;
}

algebra::node_csp parser::data::parse_with()
{
  get_next_token();
  if(tok.type == token_type::STAR)
  {
    get_next_token();
    return std::make_shared<algebra::with>(true, std::vector<algebra::named_expression_csp>(), parse_modifiers());
  }
  std::vector<algebra::named_expression_csp> named_expressions = parse_named_expressions();
  std::vector<std::string> new_variables;
  for(algebra::named_expression_csp ne : named_expressions)
  {
    new_variables.push_back(ne->get_name());
    bind_variable(ne->get_name(), ne->get_expression(), expression_analyser.start(ne->get_expression()).type, true);
  }
  for(auto it = bounded_variables.begin(); it != bounded_variables.end(); )
  {
    if(std::find(new_variables.begin(), new_variables.end(), it->first) == new_variables.end())
    {
      it = bounded_variables.erase(it);
    } else {
      ++it;
    }
  }
  gqlite_debug("With new variables {} kept variables {}", new_variables, std::views::keys(bounded_variables));
  return std::make_shared<algebra::with>(false, named_expressions, parse_modifiers());
}

algebra::node_csp parser::data::parse_unwind()
{
  get_next_token();
  algebra::node_csp expr = parse_expression();
  is_of_type(token_type::AS);
  get_next_token();
  is_of_type(token_type::IDENTIFIER);
  std::string name = tok.string;
  get_next_token();
  bind_variable(name, expr, algebra::expression_type::value, false);
  return std::make_shared<algebra::unwind>(name, expr);
}

algebra::node_csp parser::data::parse_delete()
{
  bool detach = (tok.type == token_type::DETACH);
  if(detach) get_next_token();
  is_of_type(token_type::DELETE);
  get_next_token();
  std::vector<algebra::node_csp> expressions;
  while(tok.type != token_type::END_OF_FILE)
  {
    algebra::node_csp exp = parse_expression();
    algebra::expression_type et = expression_analyser.start(exp).type;
    errors::check_condition(et == algebra::expression_type::node or et == algebra::expression_type::edge or et== algebra::expression_type::value, exception_stage::compiletime, exception_code::invalid_delete, "Can only delete node/edges.");
    expressions.push_back(exp);
    if(tok.type == token_type::COMMA)
    {
      get_next_token();
    } else
    {
      break;
    }
  }
  return std::make_shared<algebra::delete_statement>(detach, expressions);
}

algebra::node_csp parser::data::parse_set()
{
  std::vector<algebra::node_csp> nodes;
  get_next_token();
  while(tok.type != token_type::END_OF_FILE)
  {
    std::string name;
    if(tok.type == token_type::STARTBRACKET)
    {
      get_next_token();
      is_of_type(token_type::IDENTIFIER);
      name = tok.string;
      get_next_token();
      is_of_type(token_type::ENDBRACKET);
    } else {
      is_of_type(token_type::IDENTIFIER);
      name = tok.string;
    }
    get_next_token();
    std::vector<std::string> path = parse_path();

    switch(tok.type)
    {
      case token_type::EQUAL:
      {
        get_next_token();
        algebra::node_csp value = parse_expression();
        nodes.push_back(std::make_shared<algebra::set_property>(name, path, value));
        break;
      }
      case token_type::PLUS_EQUAL:
      {
        get_next_token();
        algebra::node_csp value = parse_expression();
        nodes.push_back(std::make_shared<algebra::add_property>(name, path, value));
        break;
      }
      case token_type::COLON:
      {
        if(not path.empty())
        {
          report_unexpected(tok);
        }
        std::vector<std::string> labels;
        while(tok.type == token_type::COLON)
        {
          get_next_token();
          is_of_type(token_type::IDENTIFIER);
          labels.push_back(tok.string);
          get_next_token();
        }
        nodes.push_back(std::make_shared<algebra::edit_labels>(name, labels));
        break;
      }
      default:
        report_unexpected(tok);
    }

    if(tok.type == token_type::COMMA)
    {
      get_next_token();
    } else {
      break;
    }
  }
  return std::make_shared<algebra::set>(nodes);
}

algebra::node_csp parser::data::parse_remove()
{
  std::vector<algebra::node_csp> nodes;
  get_next_token();
  while(tok.type != token_type::END_OF_FILE)
  {
    std::string name;
    if(tok.type == token_type::STARTBRACKET)
    {
      get_next_token();
      is_of_type(token_type::IDENTIFIER);
      name = tok.string;
      get_next_token();
      is_of_type(token_type::ENDBRACKET);
    } else {
      is_of_type(token_type::IDENTIFIER);
      name = tok.string;
    }
    get_next_token();
    std::vector<std::string> path = parse_path();

    switch(tok.type)
    {
      case token_type::COLON:
      {
        if(not path.empty())
        {
          report_unexpected(tok);
        }
        std::vector<std::string> labels;
        while(tok.type == token_type::COLON)
        {
          get_next_token();
          is_of_type(token_type::IDENTIFIER);
          labels.push_back(tok.string);
          get_next_token();
        }
        nodes.push_back(std::make_shared<algebra::edit_labels>(name, labels));
        break;
      }
      default:
        nodes.push_back(std::make_shared<algebra::remove_property>(name, path));
        break;
    }

    if(tok.type == token_type::COMMA)
    {
      get_next_token();
    } else {
      break;
    }
  }
  return std::make_shared<algebra::remove>(nodes);
}

algebra::node_csp parser::data::parse_return()
{
  get_next_token();
  if(tok.type == token_type::STAR)
  {
    get_next_token();
    return std::make_shared<algebra::return_statement>(true, std::vector<algebra::named_expression_csp>(), parse_modifiers());
  }
  std::vector<algebra::named_expression_csp> named_expressions = parse_named_expressions();
  for(algebra::named_expression_csp ne : named_expressions)
  {
    bind_variable(ne->get_name(), ne->get_expression(), expression_analyser.start(ne->get_expression()).type, true);
  }
  return std::make_shared<algebra::return_statement>(false, named_expressions, parse_modifiers());
}

namespace
{
  struct edge
  {
    bool active = false;
    algebra::edge_directivity directivity;
    algebra::graph_node_csp source, destination;
    std::string variable;
    std::vector<std::string> labels;
    algebra::node_csp properties;
  };
}

algebra::modifiers_csp parser::data::parse_modifiers()
{
  algebra::node_csp skip, limit;
  algebra::order_by_csp order_by;

  while(tok.type == token_type::SKIP or tok.type == token_type::LIMIT or tok.type == token_type::ORDER)
  {
    switch(tok.type)
    {
      case token_type::SKIP:
        if(skip) report_unexpected(tok);
        get_next_token();
        skip = parse_expression();
        if(skip->get_type() == algebra::node_type::value)
        {
          value v = std::static_pointer_cast<const algebra::value>(skip)->get_value();
          errors::check_argument_type(v.get_type(), value_type::integer, exception_stage::compiletime, "SKIP expect integer argument.");
          errors::check_condition(v.to_integer() >= 0, exception_stage::compiletime, exception_code::negative_integer_argument, "Arugments to SKIP should be positive.");
        }
        errors::check_constant(expression_analyser.start(skip).constant, exception_stage::compiletime, "SKIP expect a constant expression.");
        break;
      case token_type::LIMIT:
        if(limit) report_unexpected(tok);
        get_next_token();
        limit = parse_expression();
        if(limit->get_type() == algebra::node_type::value)
        {
          value v = std::static_pointer_cast<const algebra::value>(limit)->get_value();
          errors::check_argument_type(v.get_type(), value_type::integer, exception_stage::compiletime, "LIMIT expect integer argument.");
          errors::check_condition(v.to_integer() >= 0, exception_stage::compiletime, exception_code::negative_integer_argument, "Arugments to LIMIT should be positive.");
        }
        errors::check_constant(expression_analyser.start(limit).constant, exception_stage::compiletime, "LIMIT expect a constant expression.");
        break;
      case token_type::ORDER:
      {
        if(order_by) report_unexpected(tok);
        get_next_token();
        is_of_type(token_type::BY);
        get_next_token();
        std::vector<algebra::order_by_expression_csp> expressions;
        while(true)
        {
          bool asc = true;
          algebra::node_csp expression = parse_expression();
          if(tok.type == token_type::ASC)
          {
            get_next_token();
          } else if(tok.type == token_type::DESC)
          {
            asc = false;
            get_next_token();
          }
          expressions.push_back(std::make_shared<algebra::order_by_expression>(asc, expression));
          if(tok.type == token_type::COMMA)
          {
            get_next_token();
          } else {
            break;
          }
        }
        order_by = std::make_shared<algebra::order_by>(expressions);
        break;
      }
      default:
        report_unexpected(tok);
    }
  }

  if(skip or limit or order_by)
  {
    return std::make_shared<algebra::modifiers>(skip, limit, order_by);
  } else {
    return nullptr;
  }
}

std::vector<algebra::alternative<algebra::graph_node, algebra::graph_edge>> parser::data::parse_patterns(bool _creation_mode)
{
  std::vector<algebra::alternative<algebra::graph_node, algebra::graph_edge>> patterns;
  std::vector<std::string> new_node_variables;
  edge current_edge;
  do
  {
    is_of_type(token_type::STARTBRACKET);
    get_next_token();
    // identifier
    std::string variable;
    if(tok.type == token_type::IDENTIFIER)
    { 
      variable = tok.string;
      get_next_token();
    } else {
      variable = generate_anonymous_variable();
    }
    // Labels
    std::vector<std::string> labels;
    while(tok.type == token_type::COLON)
    {
      get_next_token();
      is_of_type(token_type::IDENTIFIER);
      labels.push_back(tok.string);
      get_next_token();
    }
    // Properties
    algebra::node_csp properties;
    if(tok.type == token_type::STARTBRACE or tok.type == token_type::PARAMETER)
    {
      properties = parse_properties();
    }
    // End of node
    is_of_type(token_type::ENDBRACKET);
    get_next_token();
    algebra::graph_node_csp gnode = std::make_shared<algebra::graph_node>(variable, labels, properties);
    // Handle, check if edge is active
    bool add_to_patterns = true;
    if(current_edge.active)
    {
      if(current_edge.source)
      {
        current_edge.destination = gnode;
      } else {
        current_edge.source = gnode;
      }
      algebra::graph_edge_csp ge = std::make_shared<algebra::graph_edge>(current_edge.variable, current_edge.source, current_edge.destination, current_edge.directivity, current_edge.labels, current_edge.properties);
      validate(ge);
      patterns.push_back(ge);
      current_edge.active = false;
      add_to_patterns = false;
      new_node_variables.push_back(current_edge.source->get_variable());
      new_node_variables.push_back(current_edge.destination->get_variable());
    }
    // If comma, an other node statement comes after
    if(tok.type == token_type::COMMA)
    {
      get_next_token();
    }
    else if(tok.type == token_type::MINUS or tok.type == token_type::LEFT_ARROW)
    {
      // Reset current edge
      current_edge = edge{};
      current_edge.active = true;
      // Handle directivity
      if(tok.type == token_type::MINUS)
      {
        current_edge.source = gnode;
        current_edge.directivity = algebra::edge_directivity::undirected;
      } else {
        current_edge.destination = gnode;
        current_edge.directivity = algebra::edge_directivity::directed;
      }
      // Parse
      get_next_token();
      if(tok.type == token_type::RIGHT_ARROW or tok.type == token_type::MINUS)
      {
        if(_creation_mode)
        {
          is_of_type(token_type::STARTBOXBRACKET);
        }
      } else {
        is_of_type(token_type::STARTBOXBRACKET);
        get_next_token();
        // parse edge
        if(tok.type == token_type::IDENTIFIER)
        { // identifier
          current_edge.variable = tok.string;
          get_next_token();
        } else {
          current_edge.variable = generate_anonymous_variable();
        }
        if(tok.type == token_type::COLON)
        { // label
          get_next_token();
          is_of_type(token_type::IDENTIFIER);
          current_edge.labels.push_back(tok.string);
          get_next_token();
          if(not _creation_mode)
          {
            while(tok.type != token_type::END_OF_FILE)
            {
              if(tok.type == token_type::PIPE)
              {
                get_next_token();
                if(tok.type == token_type::COLON)
                {
                  get_next_token();
                }
                is_of_type(token_type::IDENTIFIER);
                if(std::find(current_edge.labels.begin(), current_edge.labels.end(), tok.string) == current_edge.labels.end())
                {
                  current_edge.labels.push_back(tok.string);
                }
                get_next_token();
              } else {
                break;
              }
            }
          }
        } else if(_creation_mode) {
          is_of_type(token_type::COLON); //
        }
        if(tok.type == token_type::STARTBRACE or tok.type == token_type::PARAMETER)
        {
          current_edge.properties = parse_properties();
        }
        is_of_type(token_type::ENDBOXBRACKET);
        get_next_token();
      }
      if(tok.type == token_type::MINUS)
      {
        if(current_edge.directivity == algebra::edge_directivity::undirected)
        {
          if(_creation_mode)
          {
            report_error(tok, exception_code::requires_directed_relationship, "Edge must be directed during creation");
          }
        }
      } else if(tok.type == token_type::RIGHT_ARROW)
      {
        if(current_edge.directivity == algebra::edge_directivity::directed)
        {
          report_error(tok, exception_code::requires_directed_relationship, "Edge cannot have both direction");
        }
        current_edge.directivity = algebra::edge_directivity::directed;
      } else {
        report_unexpected(tok);
      }
      get_next_token();
      is_of_type(token_type::STARTBRACKET);
      add_to_patterns = false;
    } else {
      if(add_to_patterns)
      {
        if(_creation_mode and std::find(new_node_variables.begin(), new_node_variables.end(), gnode->get_variable()) == new_node_variables.end())
        {
          check_variable_existence(gnode->get_variable());
        }
        new_node_variables.push_back(gnode->get_variable());
        validate(gnode);
        patterns.push_back(gnode);
      }
      break;
    }
    if(add_to_patterns)
    {
      if(_creation_mode and std::find(new_node_variables.begin(), new_node_variables.end(), gnode->get_variable()) == new_node_variables.end())
      {
        check_variable_existence(gnode->get_variable());
      }
      new_node_variables.push_back(gnode->get_variable());
      validate(gnode);
      patterns.push_back(gnode);
    }
  } while(true);
  if(current_edge.active)
  {
    report_error(tok, exception_code::parse_error, "Unfinished edge");
  }
  return patterns;
}

algebra::node_csp parser::data::parse_map()
{
  std::unordered_map<std::string, algebra::node_csp> p;
  get_next_token(lexer::mode::disable_keywords);
  bool only_values = true;
  while(tok.type != token_type::ENDBRACE)
  {
    if(tok.type != token_type::STRING and tok.type != token_type::IDENTIFIER)
    {
      report_unexpected(tok);
    }
    std::string key = tok.string;
    get_next_token();
    is_of_type(token_type::COLON);
    get_next_token();
    algebra::node_csp n = parse_expression();
    p[key] = n;
    only_values = only_values and n->get_type() == algebra::node_type::value;
    if(tok.type == token_type::COMMA)
    {
      get_next_token(lexer::mode::disable_keywords);
    } else {
      break;
    }
  }
  is_of_type(token_type::ENDBRACE);
  get_next_token();
  if(only_values)
  {
    value_map vm;
    for(const auto& [k, v] : p)
    {
      vm[k] = std::static_pointer_cast<const algebra::value>(v)->get_value();
    }
    return std::make_shared<algebra::value>(vm);
  } else {
    return std::make_shared<algebra::map>(p);
  }
}

algebra::node_csp parser::data::parse_properties()
{
  switch(tok.type)
  {
    case token_type::STARTBRACE:
    {
      return parse_map();
    }
    case token_type::PARAMETER:
    {
      auto it = bindings.find(tok.string);
      if(it == bindings.end())
      {
        report_error(tok, exception_code::invalid_parameter_use, "Unknown parameter '{}'", tok.string);
      }
      get_next_token();
      return std::make_shared<algebra::value>(it->second);
    }
    default:
      report_unexpected(tok);
  }
}

algebra::node_csp parser::data::parse_terminal_expression()
{
  token t = tok;
  switch (tok.type)
  {
  case token_type::STARTBRACKET:
  {
    get_next_token();
    algebra::node_csp no = parse_expression();
    is_of_type(token_type::ENDBRACKET);
    get_next_token();
    return no;
  }
  case token_type::NULL_CAPS:
  case token_type::NULL_TOKEN:
    get_next_token();
    return std::make_shared<algebra::value>(gqlite::value());
  case token_type::IDENTIFIER:
    get_next_token();
    switch(tok.type)
    {
      case token_type::STARTBRACKET:
      {
        std::vector<algebra::node_csp> arguments;
        get_next_token();
        if(tok.type != token_type::ENDBRACKET)
        {
          while(tok.type != token_type::END_OF_FILE)
          {
            arguments.push_back(parse_expression());
            if(tok.type == token_type::COMMA)
            {
              get_next_token();
            } else {
              break;
            }
          }
          is_of_type(token_type::ENDBRACKET);
        }
        get_next_token();
        return std::make_shared<algebra::function_call>(t.string, arguments);
      }
      case token_type::COLON:
      {
        std::vector<std::string> labels;
        get_next_token();
        while(tok.type == token_type::IDENTIFIER)
        {
          labels.push_back(tok.string);
          get_next_token();
          if(tok.type == token_type::COLON)
          {
            get_next_token();
          } else {
            break;
          }
        }
        return std::make_shared<algebra::has_labels>(t.string, labels);
      }
      default:
        if(bounded_variables.find(t.string) == bounded_variables.end()) errors::undefined_variable(exception_stage::compiletime, t.string);
        return std::make_shared<algebra::variable>(t.string);
    }
  case token_type::STRING:
    get_next_token();
    return std::make_shared<algebra::value>(t.string);
  case token_type::INTEGER:
    get_next_token();
    return std::make_shared<algebra::value>(string_to_integer(t.string));
  case token_type::FLOATING_POINT:
    get_next_token();
    return std::make_shared<algebra::value>(std::stod(t.string));
  case token_type::TRUE:
    get_next_token();
    return std::make_shared<algebra::value>(true);
  case token_type::FALSE:
    get_next_token();
    return std::make_shared<algebra::value>(false);
  case token_type::STARTBRACE:
  {
    return parse_map();
  }
  case token_type::STARTBOXBRACKET:
  {
    return parse_expression_list();
  }
  case token_type::PARAMETER:
  {
    auto it = bindings.find(tok.string);
    if(it == bindings.end())
    {
      report_error(tok, exception_code::invalid_parameter_use, "Unknown parameter '{}'", tok.string);
    }
    get_next_token();
    return std::make_shared<algebra::value>(it->second);
  }
  default:
    report_unexpected(tok);
  }
}

algebra::node_csp parser::data::parse_expression_list()
{
  get_next_token();
  if(tok.type == token_type::ENDBOXBRACKET)
  {
    get_next_token();
    // empty list
    return std::make_shared<algebra::value>(value_vector());
  }
  std::vector<algebra::node_csp> nodes;
  bool only_values = true;
  while(tok.type != token_type::END_OF_FILE)
  {
    algebra::node_csp n = parse_expression();
    only_values = only_values and n->get_type() == algebra::node_type::value;
    nodes.push_back(n);
    if(tok.type == token_type::COMMA)
    {
      get_next_token();
    } else {
      is_of_type(token_type::ENDBOXBRACKET);
      get_next_token();
      if(only_values)
      { // If only values, can return a value, which is much simpler to evaluate than an algebra::array
        value_vector vv;
        for(algebra::node_csp n : nodes)
        {
          vv.push_back(std::static_pointer_cast<const algebra::value>(n)->get_value());
        }
        return std::make_shared<algebra::value>(vv);
      } else {
        return std::make_shared<algebra::array>(nodes);
      }
    }
  }
  report_unexpected(tok);
}

algebra::node_csp parser::data::parse_conditional_xor_expression()
{
  algebra::node_csp node = parse_conditional_or_expression();
  if(tok.type == token_type::XOR)
  {
    get_next_token();
    algebra::node_csp node_right = parse_conditional_xor_expression();
    errors::check_argument_types(expression_analyser.start(node).type, {algebra::expression_type::boolean, algebra::expression_type::value, algebra::expression_type::empty}, exception_stage::compiletime, "Requires boolean in logical and expression.");
    errors::check_argument_types(expression_analyser.start(node_right).type, {algebra::expression_type::boolean, algebra::expression_type::value, algebra::expression_type::empty}, exception_stage::compiletime, "Requires boolean in logical and expression.");
    return std::make_shared<algebra::logical_xor>(node, node_right);
  } else {
    return node;
  }
}

algebra::node_csp parser::data::parse_conditional_or_expression()
{
  algebra::node_csp node = parse_conditional_and_expression();
  if(tok.type == token_type::OR)
  {
    get_next_token();
    algebra::node_csp node_right = parse_conditional_or_expression();
    errors::check_argument_types(expression_analyser.start(node).type, {algebra::expression_type::boolean, algebra::expression_type::value, algebra::expression_type::empty}, exception_stage::compiletime, "Requires boolean in logical and expression.");
    errors::check_argument_types(expression_analyser.start(node_right).type, {algebra::expression_type::boolean, algebra::expression_type::value, algebra::expression_type::empty}, exception_stage::compiletime, "Requires boolean in logical and expression.");
    return std::make_shared<algebra::logical_or>(node, node_right);
  } else {
    return node;
  }
}

algebra::node_csp parser::data::parse_conditional_and_expression()
{
  algebra::node_csp node = parse_relational_expression();
  if(tok.type == token_type::AND)
  {
    get_next_token();
    algebra::node_csp node_right = parse_conditional_and_expression();
    errors::check_argument_types(expression_analyser.start(node).type, {algebra::expression_type::boolean, algebra::expression_type::value, algebra::expression_type::empty}, exception_stage::compiletime, "Requires boolean in logical and expression.");
    errors::check_argument_types(expression_analyser.start(node_right).type, {algebra::expression_type::boolean, algebra::expression_type::value, algebra::expression_type::empty}, exception_stage::compiletime, "Requires boolean in logical and expression.");
    return std::make_shared<algebra::logical_and>(node, node_right);
  } else {
    return node;
  }
}

algebra::node_csp parser::data::parse_relational_expression()
{
  algebra::node_csp node = parse_additive_expression();
  switch(tok.type)
  {
    case token_type::IS:
    {
      get_next_token();
      if(tok.type == token_type::NOT)
      {
        get_next_token();
        is_of_type(token_type::NULL_CAPS);
        get_next_token();
        return std::make_shared<algebra::is_not_null>(node);
      } else {
        is_of_type(token_type::NULL_CAPS);
        get_next_token();
        return std::make_shared<algebra::is_null>(node);
      }
    }
    case token_type::EQUAL:
      get_next_token();
      return std::make_shared<algebra::relational_equal>(node, parse_relational_expression());
    case token_type::DIFFERENT:
      get_next_token();
      return std::make_shared<algebra::relational_different>(node, parse_relational_expression());
    case token_type::INFERIOR:
      get_next_token();
      return std::make_shared<algebra::relational_inferior>(node, parse_relational_expression());
    case token_type::SUPERIOR:
      get_next_token();
      return std::make_shared<algebra::relational_superior>(node, parse_relational_expression());
    case token_type::INFERIOR_EQUAL:
      get_next_token();
      return std::make_shared<algebra::relational_inferior_equal>(node, parse_relational_expression());
    case token_type::SUPERIOR_EQUAL:
      get_next_token();
      return std::make_shared<algebra::relational_superior_equal>(node, parse_relational_expression());
    case token_type::IN:
      {
        get_next_token();
        algebra::node_csp container = parse_expression();
        algebra::expression_type container_type = expression_analyser.start(container).type;
        errors::check_argument_types(container_type, {algebra::expression_type::map, algebra::expression_type::vector, algebra::expression_type::value}, exception_stage::compiletime, "Relational IN can only be applied on vector/map.");
        if(container_type == algebra::expression_type::map)
        {
          errors::check_argument_types(expression_analyser.start(node).type, {algebra::expression_type::string, algebra::expression_type::value}, exception_stage::compiletime, "Relational IN on map expect a string.");
        }
        return std::make_shared<algebra::relational_in>(node, container);
      }
    case token_type::NOT:
      {
        get_next_token();
        if(is_of_type(tok, token_type::IN))
        {
          get_next_token();
        }
        algebra::node_csp container = parse_expression();
        algebra::expression_type container_type = expression_analyser.start(container).type;
        errors::check_argument_types(container_type, {algebra::expression_type::map, algebra::expression_type::vector, algebra::expression_type::value}, exception_stage::compiletime, "Relational NOT IN can only be applied on vector/map.");
        if(container_type == algebra::expression_type::map)
        {
          errors::check_argument_types(expression_analyser.start(node).type, {algebra::expression_type::string, algebra::expression_type::value}, exception_stage::compiletime, "Relational NOT IN on map expect a string.");
        }
        return std::make_shared<algebra::relational_not_in>(node, container);
      }
    default:
      return node;
  }
}

algebra::node_csp parser::data::parse_additive_expression()
{
  algebra::node_csp node = parse_multiplicative_expression();
  switch(tok.type)
  {
    case token_type::PLUS:
      get_next_token();
      return std::make_shared<algebra::addition>(node, parse_additive_expression());
    case token_type::MINUS:
      get_next_token();
      return std::make_shared<algebra::substraction>(node, parse_additive_expression());
    default:
      return node;
  }
}

algebra::node_csp parser::data::parse_multiplicative_expression()
{
  algebra::node_csp node = parse_unary_expression();
  switch(tok.type)
  {
    case token_type::STAR:
      get_next_token();
      return std::make_shared<algebra::multiplication>(node, parse_multiplicative_expression());
    case token_type::DIVIDE:
      get_next_token();
      return std::make_shared<algebra::division>(node, parse_multiplicative_expression());
    case token_type::PERCENT:
      get_next_token();
      return std::make_shared<algebra::modulo>(node, parse_multiplicative_expression());
    default:
      return node;
  }
}

algebra::node_csp parser::data::parse_unary_expression()
{
  switch(tok.type)
  {
    case token_type::NOT:
    case token_type::EXCLAMATION:
    {
      get_next_token();

      algebra::node_csp node = parse_unary_expression();
      errors::check_argument_types(expression_analyser.start(node).type, {algebra::expression_type::boolean, algebra::expression_type::value, algebra::expression_type::empty}, exception_stage::compiletime, "Requires boolean in logical and expression.");
      return std::make_shared<algebra::logical_negation>(node);
    }
    case token_type::MINUS:
    {
      get_next_token();
      token t = tok;
      switch(tok.type)
      {
        // This is needed to properly parse -9223372036854775808
      case token_type::INTEGER:
        get_next_token();
        return std::make_shared<algebra::value>(string_to_integer("-" + t.string));
      case token_type::FLOATING_POINT:
        get_next_token();
        return std::make_shared<algebra::value>(std::stod("-" + t.string));
      default:
        return std::make_shared<algebra::negation>(parse_indexed_access_expression());
      }
    }
    case token_type::PLUS:
      get_next_token();
      [[fallthrough]];
    default:
      return parse_indexed_access_expression();
  }
}

std::vector<std::string> parser::data::parse_path()
{
  std::vector<std::string> path;
  while (tok.type == token_type::DOT)
  {
    get_next_token();
    is_of_type(token_type::IDENTIFIER);
    path.push_back(tok.string);
    get_next_token();
  }
  return path;
}

algebra::node_csp parser::data::parse_indexed_access_expression()
{
  algebra::node_csp left = parse_member_expression();
  while(tok.type == token_type::STARTBOXBRACKET)
  {
    get_next_token();
    algebra::node_csp index;
    if(tok.type == token_type::DOTDOT)
    {
      index = std::make_shared<algebra::value>(int64_t(0));
    } else {
      index = parse_expression();
    }
    algebra::node_csp end = nullptr;
    if(tok.type == token_type::DOTDOT)
    {
      get_next_token();
      if(tok.type == token_type::ENDBOXBRACKET)
      {
        end = std::make_shared<algebra::end_of_list>();
      } else {
        end = parse_expression();
      }
    }
    is_of_type(token_type::ENDBOXBRACKET);
    get_next_token();
    left = std::make_shared<algebra::indexed_access>(left, index, end);
  }
  return left;
}

algebra::node_csp parser::data::parse_member_expression()
{
  algebra::node_csp left = parse_terminal_expression();
  if(tok.type == token_type::DOT)
  {
    return std::make_shared<algebra::member_access>(left, parse_path());
  }
  return left;
}

algebra::node_csp parser::data::parse_expression()
{
  return parse_conditional_xor_expression();
}

void parser::data::get_next_token(int _flags)
{
  tok = lex->next_token(_flags);
}

template<typename... _T_>
void parser::data::report_error(const token& _token, exception_code _code, const std::string& _errorMsg, const _T_&... _values)
{
  throw_exception(exception_stage::compiletime, _code, _errorMsg + " at ({}, {}).", _values..., _token.line, _token.column);
}

void parser::data::report_unexpected(const token& _token) 
{
  if(_token.string.empty())
  {
    report_error(_token, exception_code::unexpected_syntax, "Unexpected token '{}'", _token.type);
  } else {
    report_error(_token, exception_code::unexpected_syntax, "Unexpected token '{}' ('{}')", _token.type, _token.string);
  }
}

bool parser::data::is_of_type(const token& _token, token_type _type)
{
  if(_token.type == _type) return true;
  if(_token.string.empty())
  {
    report_error(_token, exception_code::unexpected_syntax, "Expected token {} got {}", _type, _token.type);
  } else {
    report_error(_token, exception_code::unexpected_syntax, "Expected token {} got {} ({})", _type, _token.type, _token.string);
  }
  return false;
}

bool parser::data::is_of_type(token_type _type)
{
  return is_of_type(tok, _type);
}

parser::parser(lexer* _lexer, const value_map& _bindings) : d(new data)
{
  d->lex = _lexer;
  d->bindings = _bindings;
  d->expression_analyser.stage = exception_stage::compiletime;
  d->expression_analyser.variables_f = [this](const std::string& _name)
  {
    auto it = d->bounded_variables.find(_name);
    if(it == d->bounded_variables.end())
    {
      errors::undefined_variable(exception_stage::compiletime, _name);
    }
    return it->second.type;
  };
}

parser::~parser()
{
  delete d;
}

algebra::node_csp parser::parse()
{
  std::vector<algebra::node_csp> nodes;
  d->get_next_token();
  while(d->tok.type != token_type::END_OF_FILE)
  {
    switch(d->tok.type)
    {
    case token_type::CREATE:
      nodes.push_back(d->parse_create());
      break;
    case token_type::MATCH:
      nodes.push_back(d->parse_match());
      break;
    case token_type::WITH:
      nodes.push_back(d->parse_with());
      break;
    case token_type::UNWIND:
      nodes.push_back(d->parse_unwind());
      break;
    case token_type::DELETE:
    case token_type::DETACH:
      nodes.push_back(d->parse_delete());
      break;
    case token_type::SET:
      nodes.push_back(d->parse_set());
      break;
    case token_type::REMOVE:
      nodes.push_back(d->parse_remove());
      break;
    case token_type::RETURN:
      nodes.push_back(d->parse_return());
      d->is_of_type(token_type::END_OF_FILE);
      break;
    case token_type::CALL:
      nodes.push_back(d->parse_call());
      break;
    default:
      d->report_unexpected(d->tok);
    }
  }
  switch(nodes.size())
  {
  case 0:
    d->report_error(token(), exception_code::parse_error, "Empty query.");
  case 1:
    return nodes.front();
  default:
    return std::make_shared<algebra::statements>(nodes);
  }
}

//END oc/parser.cpp
//BEGIN oc/token.cpp


#include <assert.h>
#include <iostream>



using namespace gqlite::oc;

const char* gqlite::oc::token_type_to_string(token_type _type)
{
  switch(_type)
  {
    using enum token_type;
    case UNFINISHED_STRING:
      return "unfinished string";
    case UNFINISHED_COMMENT:
      return "unfinished comment";
    case END_OF_FILE:
      return "end of file";
    case UNKNOWN:
      return "unknown";
    case SEMI:
      return ";";
    case STARTBRACE:
      return "{";
    case ENDBRACE:
      return "}";
    case STARTBRACKET:
      return "(";
    case ENDBRACKET:
      return ")";
    case STARTBOXBRACKET:
      return "[";
    case ENDBOXBRACKET:
      return "]";
    case PIPE:
      return "|";
    case MINUS:
      return "-";
    case COMMA:
      return ",";
    case DOT:
      return ".";
    case DOTDOT:
      return "..";
    case LEFT_ARROW:
      return "<-";
    case RIGHT_ARROW:
      return "->";
    case COLON:
      return ":";
    case COLONCOLON:
      return "::";
    case EQUAL:
      return "=";
    case DIFFERENT:
      return "!=";
    case SUPERIOR:
      return ">";
    case INFERIOR:
      return "<";
    case SUPERIOR_EQUAL:
      return ">=";
    case INFERIOR_EQUAL:
      return "<=";
    case PLUS:
      return "+";
    case STAR:
      return "*";
    case DIVIDE:
      return "/";
    case EXCLAMATION:
      return "!";
    case PLUS_EQUAL:
      return "+=";
    case PERCENT:
      return "%";
    case PARAMETER:
      return "parameter";
    case IDENTIFIER:
      return "identifier";
    case STRING:
      return "string";
    case FLOATING_POINT:
      return "floating point";
    case INTEGER:
      return "integer";
#define TOKEN_KEYWORD(_K_)  \
    case _K_:               \
      return # _K_;
#define TOKEN_KEYWORD2(_K_, _S_)  \
    case _K_:                     \
      return _S_;
    TOKEN_KEYWORD(AND)
TOKEN_KEYWORD(AS)
TOKEN_KEYWORD(ASC)
TOKEN_KEYWORD(BY)
TOKEN_KEYWORD(CALL)
TOKEN_KEYWORD(CREATE)
TOKEN_KEYWORD(DELETE)
TOKEN_KEYWORD(DESC)
TOKEN_KEYWORD(DETACH)
TOKEN_KEYWORD(FALSE)
TOKEN_KEYWORD(IN)
TOKEN_KEYWORD(IS)
TOKEN_KEYWORD(LIMIT)
TOKEN_KEYWORD(NOT)
TOKEN_KEYWORD(MATCH)
TOKEN_KEYWORD2(NULL_CAPS, "NULL")
TOKEN_KEYWORD2(NULL_TOKEN, "null")
TOKEN_KEYWORD(OR)
TOKEN_KEYWORD(ORDER)
TOKEN_KEYWORD(REMOVE)
TOKEN_KEYWORD(RETURN)
TOKEN_KEYWORD(SET)
TOKEN_KEYWORD(SKIP)
TOKEN_KEYWORD(TRUE)
TOKEN_KEYWORD(UNWIND)
TOKEN_KEYWORD(XOR)
TOKEN_KEYWORD(YIELD)
TOKEN_KEYWORD(WITH)
TOKEN_KEYWORD(WHERE)

#undef TOKEN_KEYWORD
#undef TOKEN_KEYWORD2
  }
  gqlite_fatal("add the token {} ", (int)_type);
}

token::token() : type(token_type::UNKNOWN)
{

}

token::token(token_type _type, int _line, int _column) : type(_type), line(_line), column(_column)
{
}

token::token(token_type _type, const std::string& _string, int _line, int _column) : type(_type), line(_line), column(_column), string(_string)
{
}
//END oc/token.cpp
//BEGIN oc/algebra/abstract_node_visitor.cpp


using namespace gqlite::oc::algebra::details;

abstract_node_visitor_adaptor::abstract_node_visitor_adaptor()
{

}

abstract_node_visitor_adaptor::~abstract_node_visitor_adaptor()
{

}
//END oc/algebra/abstract_node_visitor.cpp
//BEGIN oc/algebra/node_type.cpp




namespace gqlite::oc::algebra
{
  const char * node_type_name(node_type _nt)
  {
    switch(_nt)
    {
#define OC_ALGEBRA_GENERATE(_KLASS_NAME_, _MEMBER_DEF_) \
      case node_type::_KLASS_NAME_:                              \
        return #_KLASS_NAME_;
// Queries

#define OC_ALGEBRA_CREATE_NODES_MEMBERS(_KLASS_NAME_, F)                                    \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)

OC_ALGEBRA_GENERATE(create, OC_ALGEBRA_CREATE_NODES_MEMBERS)

#define OC_ALGEBRA_MATCH_NODES_MEMBERS(_KLASS_NAME_, F)                                     \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)  \
  F(_KLASS_NAME_, node_csp, where)

OC_ALGEBRA_GENERATE(match, OC_ALGEBRA_MATCH_NODES_MEMBERS)

// Statements

#define OC_ALGEBRA_NODES_LIST_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, nodes)

OC_ALGEBRA_GENERATE(statements, OC_ALGEBRA_NODES_LIST_MEMBERS)

#define OC_ALGEBRA_RETURN_MEMBERS(_KLASS_NAME_, F)                \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions) \
  F(_KLASS_NAME_, modifiers_csp, modifiers)

OC_ALGEBRA_GENERATE(return_statement, OC_ALGEBRA_RETURN_MEMBERS)

#define OC_ALGEBRA_WITH_MEMBERS(_KLASS_NAME_, F)                  \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions) \
  F(_KLASS_NAME_, modifiers_csp, modifiers)

OC_ALGEBRA_GENERATE(with, OC_ALGEBRA_WITH_MEMBERS)

#define OC_ALGEBRA_UNWIND_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, std::string, name)                \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(unwind, OC_ALGEBRA_UNWIND_MEMBERS)

#define OC_ALGEBRA_DELETE_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, bool, detach)                     \
  F(_KLASS_NAME_, std::vector<node_csp>, expressions)

OC_ALGEBRA_GENERATE(delete_statement, OC_ALGEBRA_DELETE_MEMBERS)

OC_ALGEBRA_GENERATE(set, OC_ALGEBRA_NODES_LIST_MEMBERS)
OC_ALGEBRA_GENERATE(remove, OC_ALGEBRA_NODES_LIST_MEMBERS)

#define OC_ALGEBRA_CALL_MEMBERS(_KLASS_NAME_, F)    \
  F(_KLASS_NAME_, std::string, name)                \
  F(_KLASS_NAME_, std::vector<node_csp>, arguments) \
  F(_KLASS_NAME_, std::vector<std::string>, yield)

OC_ALGEBRA_GENERATE(call, OC_ALGEBRA_CALL_MEMBERS)


#define OC_ALGEBRA_ORDER_BY_MEMBERS(_KLASS_NAME_, F)                  \
  F(_KLASS_NAME_, std::vector<order_by_expression_csp>, expressions)

OC_ALGEBRA_GENERATE(order_by, OC_ALGEBRA_ORDER_BY_MEMBERS)

#define OC_ALGEBRA_MODIFIERS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, skip)                     \
  F(_KLASS_NAME_, node_csp, limit)                    \
  F(_KLASS_NAME_, order_by_csp, order_by)

OC_ALGEBRA_GENERATE(modifiers, OC_ALGEBRA_MODIFIERS_MEMBERS)

// Order By Expression

#define OC_ALGEBRA_ORDER_BY_EXPRESSION_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, bool, asc)                          \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(order_by_expression, OC_ALGEBRA_ORDER_BY_EXPRESSION_MEMBERS)

// Set/remove Expressions

#define OC_ALGEBRA_SET_ADD_VALUE_MEMBERS(_KLASS_NAME_, F)       \
  F(_KLASS_NAME_, std::string, left)                            \
  F(_KLASS_NAME_, std::vector<std::string>, path)               \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(set_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)


#define OC_ALGEBRA_SET_EQUAL_MEMBERS(_KLASS_NAME_, F)                     \
  F(_KLASS_NAME_, std::string, left)                            \
  F(_KLASS_NAME_, std::vector<std::string>, path)               \
  F(_KLASS_NAME_, nocde_csp, expression)

OC_ALGEBRA_GENERATE(add_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)

#define OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS(_KLASS_NAME_, F)     \
  F(_KLASS_NAME_, std::string, left)                            \
  F(_KLASS_NAME_, std::vector<std::string>, path)

OC_ALGEBRA_GENERATE(remove_property, OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS)

#define OC_ALGEBRA_EDIT_LABELS_MEMBERS(_KLASS_NAME_, F)                   \
  F(_KLASS_NAME_, std::string, target)                                    \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(edit_labels, OC_ALGEBRA_EDIT_LABELS_MEMBERS)


// Values

#define OC_ALGEBRA_NO_MEMBERS(_KLASS_NAME_, F)

OC_ALGEBRA_GENERATE(end_of_list, OC_ALGEBRA_NO_MEMBERS)

#define OC_ALGEBRA_GRAPH_NODE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, node_csp, properties)

OC_ALGEBRA_GENERATE(graph_node, OC_ALGEBRA_GRAPH_NODE_MEMBERS)

#define OC_ALGEBRA_GRAPH_EDGE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, graph_node_csp, source)                       \
  F(_KLASS_NAME_, graph_node_csp, destination)                  \
  F(_KLASS_NAME_, edge_directivity, directivity)                \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, node_csp, properties)

OC_ALGEBRA_GENERATE(graph_edge, OC_ALGEBRA_GRAPH_EDGE_MEMBERS)

#define OC_ALGEBRA_VALUE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, gqlite::value, value)

OC_ALGEBRA_GENERATE(value, OC_ALGEBRA_VALUE_MEMBERS)


#define OC_ALGEBRA_MAP_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::unordered_map<GQLITE_LIST(std::string, node_csp)>, map)

OC_ALGEBRA_GENERATE(map, OC_ALGEBRA_MAP_MEMBERS)

#define OC_ALGEBRA_ARRAY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, array)

OC_ALGEBRA_GENERATE(array, OC_ALGEBRA_ARRAY_MEMBERS)


// Expressions

#define OC_ALGEBRA_NAMED_EXPRESSION(_KLASS_NAME_, F)      \
  F(_KLASS_NAME_, std::string, name)                      \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(named_expression, OC_ALGEBRA_NAMED_EXPRESSION)

#define OC_ALGEBRA_VARIABLE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, identifier)

OC_ALGEBRA_GENERATE(variable, OC_ALGEBRA_VARIABLE_MEMBERS)

#define OC_ALGEBRA_MEMBER_ACCESS_MEMBERS(_KLASS_NAME_, F)   \
  F(_KLASS_NAME_, node_csp, left)                           \
  F(_KLASS_NAME_, std::vector<std::string>, path)

OC_ALGEBRA_GENERATE(member_access, OC_ALGEBRA_MEMBER_ACCESS_MEMBERS)

#define OC_ALGEBRA_INDEXED_ACCESS_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, node_csp, left)                           \
  F(_KLASS_NAME_, node_csp, index)                          \
  F(_KLASS_NAME_, node_csp, end)

OC_ALGEBRA_GENERATE(indexed_access, OC_ALGEBRA_INDEXED_ACCESS_MEMBERS)

#define OC_ALGEBRA_HAS_LABELS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, left)                   \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(has_labels, OC_ALGEBRA_HAS_LABELS_MEMBERS)

#define OC_ALGEBRA_FUNCTION_CALL_EXPRESSION(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, std::string, name)                          \
  F(_KLASS_NAME_, std::vector<node_csp>, arguments)

OC_ALGEBRA_GENERATE(function_call, OC_ALGEBRA_FUNCTION_CALL_EXPRESSION)

#define OC_ALGEBRA_BINARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, left)                  \
  F(_KLASS_NAME_, node_csp, right)

OC_ALGEBRA_GENERATE(logical_and, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(logical_or, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(logical_xor, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_different, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_in, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_not_in, OC_ALGEBRA_BINARY_MEMBERS)

OC_ALGEBRA_GENERATE(addition, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(substraction, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(multiplication, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(division, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(modulo, OC_ALGEBRA_BINARY_MEMBERS)

#define OC_ALGEBRA_UNARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, value)

OC_ALGEBRA_GENERATE(logical_negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_null, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_not_null, OC_ALGEBRA_UNARY_MEMBERS)

#undef OC_ALGEBRA_GENERATE
      case node_type::__Invalid__:
        return "__Invalid__";
    }
    gqlite_fatal("impossible");
  }
}

//END oc/algebra/node_type.cpp
//BEGIN oc/algebra/node.cpp


using namespace gqlite::oc::algebra;

node::node(node_type _type, node::data* _d) : d(_d)
{
  d->type = _type;
}

node::~node()
{
  delete d;
}

node_type node::get_type() const
{
  return d->type;
}
//END oc/algebra/node.cpp
//BEGIN oc/algebra/nodes.cpp






namespace gqlite::oc::algebra::details
{
  template<typename _T_>
  bool equals(const _T_& _a, const _T_& _b) requires std::same_as<_T_, gqlite::value> or std::same_as<_T_, std::string> or std::same_as<_T_, edge_directivity> or std::is_integral_v<_T_> or std::is_floating_point_v<_T_>
  {
    return _a == _b;
  }
  template<typename _T_>
  bool equals(const std::shared_ptr<const _T_>& _a, const std::shared_ptr<const _T_>& _b) requires std::derived_from<_T_, node>
  {
    return (_a == _b) or (_a and _b and _a->equals(_b));
  }
  template<typename... _T_>
  bool equals(const alternative<_T_...>& _a, const alternative<_T_...>& _b)
  {
    return _a.get_node()->equals(_b.get_node());
  }
  template<typename _T1_, typename _T2_>
  bool equals(const std::pair<_T1_, _T2_>& _a, const std::pair<_T1_, _T2_>& _b)
  {
    return equals(_a.first, _b.first) and equals(_a.second, _b.second);
  }
  template<typename _T_>
  bool equals(const std::vector<_T_>& _a, const std::vector<_T_>& _b)
  {
    if(_a.size() != _b.size()) return false;
    for(std::size_t i = 0; i < _a.size(); ++i)
    {
      if(not equals(_a[i], _b[i])) return false;
    }
    return true;
  }
  template<typename _T_>
  bool equals(const std::unordered_map<std::string, _T_>& _a, const std::unordered_map<std::string, _T_>& _b)
  {
    if(_a.size() != _b.size()) return false;
    for(auto const& [k,v] : _a)
    {
      auto it = _b.find(k);
      if(it == _b.end() or not equals(v, it->second)) return false;
    }
    return true;
  }
}

namespace gqlite::oc::algebra {

#define OC_ALGEBRA_GENERATE_PRIVATE_MEMBER(_KLASS_NAME_, _TYPE_, _NAME_) \
  _TYPE_ m_ ## _NAME_;

#define OC_ALGEBRA_GENERATE_ACCESSOR_DEFINITION(_KLASS_NAME_, _TYPE_, _NAME_)   \
  _TYPE_ _KLASS_NAME_::get_ ## _NAME_() const                                   \
  {                                                                             \
    return static_cast<data*>(d)->m_ ## _NAME_;                                 \
  }

#define GQL_GENERATE_DESTRUCTOR(_KLASS_NAME_, _TYPE_, _NAME_)
  
#define OC_ALGEBRA_GENERATE_ASSIGNMENT(_KLASS_NAME_, _TYPE_, _NAME_)  \
  static_cast<data*>(d)->m_ ## _NAME_ = _ ## _NAME_;

#define OC_ALGEBRA_GENERATE_COMPARISON(_KLASS_NAME_, _TYPE_, _NAME_)  \
  and details::equals(static_cast<data*>(d)->m_ ## _NAME_, static_cast<data*>(rhs->d)->m_ ## _NAME_)

#define OC_ALGEBRA_GENERATE(_KLASS_NAME_, _MEMBER_DEF_)                                                                                                 \
struct _KLASS_NAME_::data : public node::data                                                                                                           \
{                                                                                                                                                       \
  _MEMBER_DEF_(_KLASS_NAME_, OC_ALGEBRA_GENERATE_PRIVATE_MEMBER)                                                                                        \
};                                                                                                                                                      \
                                                                                                                                                        \
_KLASS_NAME_::_KLASS_NAME_(_MEMBER_DEF_(_KLASS_NAME_, OC_ALGEBRA_GENERATE_CONSTRUCTOR_ARGUMENT) void* _)                                                \
  : node(node_type::_KLASS_NAME_, new data)                                                                                                             \
{                                                                                                                                                       \
  GQLITE_UNUSED(_);                                                                                                                                     \
  _MEMBER_DEF_(_KLASS_NAME_, OC_ALGEBRA_GENERATE_ASSIGNMENT)                                                                                            \
}                                                                                                                                                       \
                                                                                                                                                        \
_KLASS_NAME_::~_KLASS_NAME_()                                                                                                                           \
{                                                                                                                                                       \
  _MEMBER_DEF_(_KLASS_NAME_, GQL_GENERATE_DESTRUCTOR)                                                                                                   \
}                                                                                                                                                       \
                                                                                                                                                        \
bool _KLASS_NAME_::equals(const std::shared_ptr<const node>& _node) const                                                                               \
{                                                                                                                                                       \
  if(_node->get_type() != node_type::_KLASS_NAME_) return false;                                                                                        \
  _KLASS_NAME_ ## _csp rhs = std::static_pointer_cast<const _KLASS_NAME_>(_node);                                                                       \
  return true _MEMBER_DEF_(_KLASS_NAME_, OC_ALGEBRA_GENERATE_COMPARISON);                                                                               \
}                                                                                                                                                       \
                                                                                                                                                        \
_MEMBER_DEF_(_KLASS_NAME_, OC_ALGEBRA_GENERATE_ACCESSOR_DEFINITION)                                                                                     \
                                                                                                                                                        \
void _KLASS_NAME_::accept(details::abstract_node_visitor_adaptor* _visitor_adaptor, void* _r, void* _parameter) const                                   \
{                                                                                                                                                       \
  return _visitor_adaptor->call_visit(std::static_pointer_cast<const _KLASS_NAME_>(shared_from_this()), _r, _parameter);                                \
}

// Queries

#define OC_ALGEBRA_CREATE_NODES_MEMBERS(_KLASS_NAME_, F)                                    \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)

OC_ALGEBRA_GENERATE(create, OC_ALGEBRA_CREATE_NODES_MEMBERS)

#define OC_ALGEBRA_MATCH_NODES_MEMBERS(_KLASS_NAME_, F)                                     \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)  \
  F(_KLASS_NAME_, node_csp, where)

OC_ALGEBRA_GENERATE(match, OC_ALGEBRA_MATCH_NODES_MEMBERS)

// Statements

#define OC_ALGEBRA_NODES_LIST_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, nodes)

OC_ALGEBRA_GENERATE(statements, OC_ALGEBRA_NODES_LIST_MEMBERS)

#define OC_ALGEBRA_RETURN_MEMBERS(_KLASS_NAME_, F)                \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions) \
  F(_KLASS_NAME_, modifiers_csp, modifiers)

OC_ALGEBRA_GENERATE(return_statement, OC_ALGEBRA_RETURN_MEMBERS)

#define OC_ALGEBRA_WITH_MEMBERS(_KLASS_NAME_, F)                  \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions) \
  F(_KLASS_NAME_, modifiers_csp, modifiers)

OC_ALGEBRA_GENERATE(with, OC_ALGEBRA_WITH_MEMBERS)

#define OC_ALGEBRA_UNWIND_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, std::string, name)                \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(unwind, OC_ALGEBRA_UNWIND_MEMBERS)

#define OC_ALGEBRA_DELETE_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, bool, detach)                     \
  F(_KLASS_NAME_, std::vector<node_csp>, expressions)

OC_ALGEBRA_GENERATE(delete_statement, OC_ALGEBRA_DELETE_MEMBERS)

OC_ALGEBRA_GENERATE(set, OC_ALGEBRA_NODES_LIST_MEMBERS)
OC_ALGEBRA_GENERATE(remove, OC_ALGEBRA_NODES_LIST_MEMBERS)

#define OC_ALGEBRA_CALL_MEMBERS(_KLASS_NAME_, F)    \
  F(_KLASS_NAME_, std::string, name)                \
  F(_KLASS_NAME_, std::vector<node_csp>, arguments) \
  F(_KLASS_NAME_, std::vector<std::string>, yield)

OC_ALGEBRA_GENERATE(call, OC_ALGEBRA_CALL_MEMBERS)


#define OC_ALGEBRA_ORDER_BY_MEMBERS(_KLASS_NAME_, F)                  \
  F(_KLASS_NAME_, std::vector<order_by_expression_csp>, expressions)

OC_ALGEBRA_GENERATE(order_by, OC_ALGEBRA_ORDER_BY_MEMBERS)

#define OC_ALGEBRA_MODIFIERS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, skip)                     \
  F(_KLASS_NAME_, node_csp, limit)                    \
  F(_KLASS_NAME_, order_by_csp, order_by)

OC_ALGEBRA_GENERATE(modifiers, OC_ALGEBRA_MODIFIERS_MEMBERS)

// Order By Expression

#define OC_ALGEBRA_ORDER_BY_EXPRESSION_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, bool, asc)                          \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(order_by_expression, OC_ALGEBRA_ORDER_BY_EXPRESSION_MEMBERS)

// Set/remove Expressions

#define OC_ALGEBRA_SET_ADD_VALUE_MEMBERS(_KLASS_NAME_, F)       \
  F(_KLASS_NAME_, std::string, left)                            \
  F(_KLASS_NAME_, std::vector<std::string>, path)               \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(set_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)


#define OC_ALGEBRA_SET_EQUAL_MEMBERS(_KLASS_NAME_, F)                     \
  F(_KLASS_NAME_, std::string, left)                            \
  F(_KLASS_NAME_, std::vector<std::string>, path)               \
  F(_KLASS_NAME_, nocde_csp, expression)

OC_ALGEBRA_GENERATE(add_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)

#define OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS(_KLASS_NAME_, F)     \
  F(_KLASS_NAME_, std::string, left)                            \
  F(_KLASS_NAME_, std::vector<std::string>, path)

OC_ALGEBRA_GENERATE(remove_property, OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS)

#define OC_ALGEBRA_EDIT_LABELS_MEMBERS(_KLASS_NAME_, F)                   \
  F(_KLASS_NAME_, std::string, target)                                    \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(edit_labels, OC_ALGEBRA_EDIT_LABELS_MEMBERS)


// Values

#define OC_ALGEBRA_NO_MEMBERS(_KLASS_NAME_, F)

OC_ALGEBRA_GENERATE(end_of_list, OC_ALGEBRA_NO_MEMBERS)

#define OC_ALGEBRA_GRAPH_NODE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, node_csp, properties)

OC_ALGEBRA_GENERATE(graph_node, OC_ALGEBRA_GRAPH_NODE_MEMBERS)

#define OC_ALGEBRA_GRAPH_EDGE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, graph_node_csp, source)                       \
  F(_KLASS_NAME_, graph_node_csp, destination)                  \
  F(_KLASS_NAME_, edge_directivity, directivity)                \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, node_csp, properties)

OC_ALGEBRA_GENERATE(graph_edge, OC_ALGEBRA_GRAPH_EDGE_MEMBERS)

#define OC_ALGEBRA_VALUE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, gqlite::value, value)

OC_ALGEBRA_GENERATE(value, OC_ALGEBRA_VALUE_MEMBERS)


#define OC_ALGEBRA_MAP_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::unordered_map<GQLITE_LIST(std::string, node_csp)>, map)

OC_ALGEBRA_GENERATE(map, OC_ALGEBRA_MAP_MEMBERS)

#define OC_ALGEBRA_ARRAY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, array)

OC_ALGEBRA_GENERATE(array, OC_ALGEBRA_ARRAY_MEMBERS)


// Expressions

#define OC_ALGEBRA_NAMED_EXPRESSION(_KLASS_NAME_, F)      \
  F(_KLASS_NAME_, std::string, name)                      \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(named_expression, OC_ALGEBRA_NAMED_EXPRESSION)

#define OC_ALGEBRA_VARIABLE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, identifier)

OC_ALGEBRA_GENERATE(variable, OC_ALGEBRA_VARIABLE_MEMBERS)

#define OC_ALGEBRA_MEMBER_ACCESS_MEMBERS(_KLASS_NAME_, F)   \
  F(_KLASS_NAME_, node_csp, left)                           \
  F(_KLASS_NAME_, std::vector<std::string>, path)

OC_ALGEBRA_GENERATE(member_access, OC_ALGEBRA_MEMBER_ACCESS_MEMBERS)

#define OC_ALGEBRA_INDEXED_ACCESS_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, node_csp, left)                           \
  F(_KLASS_NAME_, node_csp, index)                          \
  F(_KLASS_NAME_, node_csp, end)

OC_ALGEBRA_GENERATE(indexed_access, OC_ALGEBRA_INDEXED_ACCESS_MEMBERS)

#define OC_ALGEBRA_HAS_LABELS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, left)                   \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(has_labels, OC_ALGEBRA_HAS_LABELS_MEMBERS)

#define OC_ALGEBRA_FUNCTION_CALL_EXPRESSION(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, std::string, name)                          \
  F(_KLASS_NAME_, std::vector<node_csp>, arguments)

OC_ALGEBRA_GENERATE(function_call, OC_ALGEBRA_FUNCTION_CALL_EXPRESSION)

#define OC_ALGEBRA_BINARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, left)                  \
  F(_KLASS_NAME_, node_csp, right)

OC_ALGEBRA_GENERATE(logical_and, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(logical_or, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(logical_xor, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_different, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_in, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_not_in, OC_ALGEBRA_BINARY_MEMBERS)

OC_ALGEBRA_GENERATE(addition, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(substraction, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(multiplication, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(division, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(modulo, OC_ALGEBRA_BINARY_MEMBERS)

#define OC_ALGEBRA_UNARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, value)

OC_ALGEBRA_GENERATE(logical_negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_null, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_not_null, OC_ALGEBRA_UNARY_MEMBERS)


#undef OC_ALGEBRA_GENERATE
}
//END oc/algebra/nodes.cpp
//BEGIN oc/algebra/visitors/print.cpp


#include <iostream>
#include <type_traits>



using namespace gqlite::oc::algebra::visitors;

namespace
{
  static void print_h(const std::string& _indentation, const std::string& _message)
  {
    std::cerr << _indentation << _message << std::endl;;
  }
  template<typename _T_, typename... _TOther_>
  static void print_h(const std::string& _indentation, const std::string& _message, _T_ const& _v, _TOther_... _other)
  {
    print_h(_indentation, gqlite::format_string(_message, _v, _other...));
  }
}

  
namespace gqlite::oc::algebra::visitors::details
{
  
  template < template <typename...> class Template, typename T >
  struct is_specialisation_of : std::false_type {};

  template < template <typename...> class Template, typename... Args >
  struct is_specialisation_of< Template, Template<Args...> > : std::true_type {};
  
  template<typename _T_>
  struct remove_const_explicitly_shared_data_pointer { typedef _T_ type; };
  template<typename _T_>
  struct remove_const_explicitly_shared_data_pointer<std::shared_ptr<const _T_>> { typedef _T_ type; };
  
  template<typename... _T_>
  struct print_helper<gqlite::oc::algebra::alternative<_T_...>>
  {
    static void call(print* _visitor, std::string& _indentation, const char* _name, const gqlite::oc::algebra::alternative<_T_...>& _t)
    {
      if(_t.is_valid())
      {
        if(_name != 0)
        {
          print_h(_indentation, "{}:", _name);
        }
        std::string indentation = _indentation;
        _indentation += "  ";
        _visitor->accept(_t.get_node());
        _indentation = indentation;
      }
    }
  };
  template<typename _T_>
  struct print_helper<std::shared_ptr<const _T_>, typename std::enable_if<std::is_base_of<gqlite::oc::algebra::node, _T_>::value>::type>
  {
    static void call(print* _visitor, std::string& _indentation, const char* _name, std::shared_ptr<const _T_> _t)
    {
      if(_t)
      {
        if(_name != 0)
        {
          print_h(_indentation, "{}:", _name);
        }
        std::string indentation = _indentation;
        _indentation += "  ";
        _visitor->accept(_t);
        _indentation = indentation;
      }
    }
  };
  template<typename _T_, typename>
  struct print_helper
  {
    static void call(print* _visitor, std::string& _indentation, const char* _name, const _T_& _t)
    {
      GQLITE_UNUSED(_visitor);
      print_h(_indentation, "{}: {}", _name, _t);
    }
  };
  template<typename _T_>
  struct print_helper<std::vector<_T_>, void>
  {
    static void call(print* _visitor, std::string& _indentation, const char* _name, const std::vector<_T_>& _t)
    {
      print_h(_indentation, "{} ({}): [", _name, _t.size());
      for(const _T_& t : _t)
      {
        print_helper<_T_>::call(_visitor, _indentation, "null", t);
      }
      print_h(_indentation, "]");
    }
  };
  template<typename _K_, typename _V_>
  struct print_helper<std::unordered_map<_K_, _V_>, void>
  {
    static void call(print* _visitor, std::string& _indentation, const char* _name, const std::unordered_map<_K_, _V_>& _t)
    {
      print_h(_indentation, "{} ({}): [", _name, _t.size());
      for(auto const& [k,v] : _t)
      {
        print_helper<_K_>::call(_visitor, _indentation, "null", k);
        std::string indentation = _indentation;
        _indentation += "  ";
        print_helper<_V_>::call(_visitor, _indentation, "null", v);
        _indentation = indentation;
      }
      print_h(_indentation, "]");
    }
  };
}

struct print::data
{
  std::string indentation;
};

print::print() : d(new data)
{

}

print::~print()
{
  delete d;
}

#define GQL_GENERATE_VISIT_CALL(_KLASS_NAME_, _TYPE_, _NAME_)          \
  details::print_helper<_TYPE_>::call(this, d->indentation, # _NAME_, _node->get_ ## _NAME_());

#define OC_ALGEBRA_GENERATE(_KLASS_NAME_, _MEMBER_DEF_)                 \
void print::visit(_KLASS_NAME_ ## _csp _node)                           \
{                                                                       \
  GQLITE_UNUSED(_node);                                                 \
  print_h(d->indentation, # _KLASS_NAME_);                              \
  std::string oldindentation = d->indentation;                          \
  d->indentation += "  ";                                               \
  _MEMBER_DEF_(_KLASS_NAME_, GQL_GENERATE_VISIT_CALL)                   \
  d->indentation = oldindentation;                                      \
}



#undef OC_ALGEBRA_GENERATE
//END oc/algebra/visitors/print.cpp
//BEGIN backends/sqlite.cpp


#include <list>
#include <map>
#include <optional>
#include <variant>

#include <sqlite3.h>









using namespace std::string_literals;
using namespace gqlite::backends;

namespace gqlite::backends
{
  void initialise_sqlite_ext(sqlite3* db);
  struct sqlite_data
  {
    sqlite3* handle;
    std::unordered_map<int64_t, std::string> id_to_label = {{0, std::string()}};
    std::unordered_map<std::string, int64_t> label_to_id = {{std::string(), 0}};

    std::unordered_map<std::string, std::function<value(const value_vector&)>> procedures;

    void throw_last_error(const std::string& _query);
    void graph_create(const std::string& _name);
    bool graph_has(const std::string& _name);
    bool table_has(const std::string& _name);

    value execute_sql(const std::string& _query, const std::map<int, value>& _bindings = {});
    void clean_up();
    uint64_t last_row_id();
    int64_t id_for_label(const std::string& _string);
    std::string label_for_id(int64_t _id);
  };

  bool is_number(oc::algebra::expression_type _et)
  {
    return _et == oc::algebra::expression_type::integer or _et == oc::algebra::expression_type::floating_point;
  }
}

struct sqlite::data : public sqlite_data
{};

void sqlite_data::throw_last_error(const std::string& _query)
{
  std::string err_msg = sqlite3_errmsg(handle);
  if(err_msg.starts_with("__gqlite_fun: "))
  {
    value_map vm = value::from_json(err_msg.substr(sizeof("__gqlite_fun: ")-1)).to_map();
    throw_exception(exception_stage::runtime, exception_code(vm["code"].to_integer()), vm["message"].to_string());
  } else {
    throw_exception(exception_stage::runtime, exception_code::internal_error, "Sqlite error '{}', while executing {}.", err_msg, _query);
  }
}

gqlite::value sqlite_data::execute_sql(const std::string& _query, const std::map<int, value>& _bindings)
{
#if 0
  std::cout << "Executing query: '" << _query << "' with bindings:" << std::endl;
  for(auto const& [k,v] : _bindings)
  {
    std::cout << " [" << k << "] = " << v.to_json() << std::endl;
  }
#endif
  const char* ptr = _query.data();
  const char* ptr_end = _query.data() + _query.size();
  value_vector q_rs;
  while(ptr != ptr_end)
  {
    sqlite3_stmt* ps;
    if(sqlite3_prepare_v2(handle, ptr, ptr_end - ptr, &ps, &ptr) != SQLITE_OK)
    {
      sqlite3_finalize(ps);
      throw_last_error(_query);
    }
    for(const auto& [key, value] : _bindings)
    {
      switch(value.get_type())
      {
        case value_type::invalid:
          sqlite3_bind_null(ps, key);
          break;
        case value_type::boolean:
          sqlite3_bind_int64(ps, key, value.to_bool() ? 1 : 0);
          break;
        case value_type::integer:
          sqlite3_bind_int64(ps, key, value.to_integer());
          break;
        case value_type::floating_point:
          sqlite3_bind_double(ps, key, value.to_double());
          break;
        default:
        {
          std::string js;
          if(value.get_type() == value_type::string)
          {
            js = value.to_string();
          } else {
            js = value.to_json();
          }
          char* arr = new char[js.size()];
          std::copy(js.begin(), js.end(), arr);
          sqlite3_bind_text(ps, key, arr, js.size(), [](void* _ptr) { delete[] static_cast<char*>(_ptr); });
          break;
        }
      }
    }
    value_vector rows;
    int code = sqlite3_step(ps);
    while(code == SQLITE_ROW)
    {
      value_vector row;
      for(int i = 0; i < sqlite3_data_count(ps); ++i)
      {
        switch(sqlite3_column_type(ps, i))
        {
          case SQLITE_INTEGER:
            row.push_back(int64_t(sqlite3_column_int64(ps, i)));
            break;
          case SQLITE_FLOAT:
            row.push_back(sqlite3_column_double(ps, i));
            break;
          case SQLITE_BLOB:
            sqlite3_finalize(ps);
            throw_exception(exception_stage::runtime, exception_code::internal_error, "Blobs are not supported.");
          case SQLITE_NULL:
            row.push_back(value());
            break;
          case SQLITE3_TEXT:
            row.push_back(std::string(reinterpret_cast<const char*>(sqlite3_column_text(ps, i))));
            break;
        }
      }
      rows.push_back(row);
      code = sqlite3_step(ps);
    }
    // Finalize the execution of the statement
    sqlite3_finalize(ps);
    switch(code)
    {
    case SQLITE_DONE:
      q_rs.push_back(rows);
      break;
    default:
      throw_last_error(_query);
    }
  }
  if(q_rs.size() == 1)
  {
    return q_rs[0];
  } else {
    return q_rs;
  }
}

void sqlite_data::clean_up()
{
  value temp = execute_sql("SELECT name FROM sqlite_temp_master;");
  for(const value& tv : temp.to_vector())
  {
    execute_sql(format_string("DROP TABLE {}", tv.to_vector().front().to_string()));
  }
}

void sqlite_data::graph_create(const std::string& _name)
{
  execute_sql(sqlite_queries::graph_create(_name));
}

bool sqlite_data::graph_has(const std::string& _name)
{
  value r= execute_sql(sqlite_queries::graph_has(_name));
  value_vector v = r.to_vector();
  errors::check_condition(v.size() == 1, exception_stage::runtime, exception_code::internal_error, "Should have gotten only one result");
  v = v.begin()->to_vector();
  errors::check_condition(v.size() == 1, exception_stage::runtime, exception_code::internal_error, "Should have gotten only one result");
  return v.begin()->to_bool();
}

bool sqlite_data::table_has(const std::string& _name)
{
  value r = execute_sql(sqlite_queries::table_has(), {{1, _name}});
  value_vector v = r.to_vector();
  errors::check_condition(v.size() == 1, exception_stage::runtime, exception_code::internal_error, "Should have gotten only one result for table_has.");
  v = v.begin()->to_vector();
  errors::check_condition(v.size() == 1, exception_stage::runtime, exception_code::internal_error, "Should have gotten only one column for table_has.");
  return v.begin()->to_bool();
}


uint64_t sqlite_data::last_row_id()
{
  return sqlite3_last_insert_rowid(handle);
}

int64_t sqlite_data::id_for_label(const std::string& _string)
{
  auto it = label_to_id.find(_string);
  if(it == label_to_id.end())
  {
    value r = execute_sql(sqlite_queries::label_get_from_name(), {{1, _string}});
    value_vector v = r.to_vector();
    if(v.size() == 0)
    {
      execute_sql(sqlite_queries::label_insert(), {{1, _string}});
      int id = last_row_id();
      id_to_label[id] = _string;
      label_to_id[_string] = id;
      return id;
    } else {
      v = v.begin()->to_vector();
      errors::check_condition(v.size() == 1, exception_stage::runtime, exception_code::internal_error, "Should have gotten only one column for label_get_from_id.");
      return v.begin()->to_integer();
    }
  } else {
    return it->second;
  }
}

std::string sqlite_data::label_for_id(int64_t _id)
{
  auto it = id_to_label.find(_id);
  if(it == id_to_label.end())
  {
    value r = execute_sql(sqlite_queries::label_get_from_id(), {{1, _id}});
    value_vector v = r.to_vector();
    if(v.size() == 0)
    {
      throw_exception(exception_stage::runtime, exception_code::internal_error, "Unknown label id {}.", _id);
    } else {
      v = v.begin()->to_vector();
      errors::check_condition(v.size() == 1, exception_stage::runtime, exception_code::internal_error, "Should have gotten only one column for label_get_from_id.");
      return v.begin()->to_string();
    }
  } else {
    return it->second;
  }
}

namespace gqlite::backends::sqlite_oc_executor
{
  namespace algebra = gqlite::oc::algebra;

  /**
   * Global context for the execution of a query.
   */
  struct execution_context
  {
    sqlite_data* data;
    std::string graph_name = "default";
    int next_view_id = 0;
  };

  struct sql_variable_info
  {
    std::string sql_var_name; ///< name of the sql variable that holds the OC variable
    oc::algebra::expression_type type;
    std::string sql_properties_var_name = std::string(); ///< name of the sql variable that holds the the properties of a node or an edge
    std::string sql_labels_var_name = std::string(); ///< 
  };

  struct table
  {
    std::string view_name;
    std::unordered_map<std::string, sql_variable_info> variables;
  };
  enum class sql_join
  {
    inner, left
  };
  struct sql_query_builder
  {
    std::size_t uid = 0;
    std::size_t table_count = 0;
    std::string variables;
    std::string tables;
    std::string join_conditions;
    std::string where_conditions;
    std::string modifiers;
    std::map<int, value> bindings;
    std::size_t next_uid()
    {
      return uid++;
    }
    std::string bind_value(const value& _value)
    {
      std::size_t binding_id = bindings.size() + 1;
      bindings[binding_id] = _value;
      return format_string("?{}", to_string_fixed_width(binding_id, 3));
    }
    /**
     * Add a new variable with a generic name format.
     */
    std::string add_variable(const std::string& _expression, const char* _format = "var_{}")
    {
      std::string name = format_string(_format, next_uid());
      return add_variable_with_name(_expression, name);
    }
    /**
     * Add a new variable with the given name.
     */
    std::string add_variable_with_name(const std::string& _expression, const std::string& _name)
    {
      if(not variables.empty()) variables += ", ";
      variables += format_string("{} AS {}", _expression, _name);
      return _name;
    }
    std::string add_table(const std::string& _table, sql_join _join, const char* _table_name_format= "tbl_{}")
    {
      if(table_count == 0)
      {
        errors::check_condition(_join == sql_join::inner, exception_stage::runtime, exception_code::internal_error, "Joining with non-inner join on first table.");
        tables += " FROM ";
      } else {
        switch(_join)
        {
          case sql_join::inner:
            tables += " JOIN ";
            break;
          case sql_join::left:
            tables += " LEFT JOIN ";
            break;
        }
      }
      ++table_count;
      std::string table_name = format_string(_table_name_format, next_uid());
      tables += format_string(" {} {} ", _table, table_name);
      return table_name;
    }
    template<typename... _T_>
    void add_join_condition(const std::string& _condition, const _T_&... _args)
    {
      if(_condition.empty()) return;
      if(not join_conditions.empty()) join_conditions += " AND ";
      join_conditions += format_string(_condition, _args...);
    }
    template<typename... _T_>
    void add_where_condition(const std::string& _condition, const _T_&... _args)
    {
      if(_condition.empty()) return;
      where_conditions += where_conditions.empty() ? " WHERE " : " AND ";
      where_conditions += format_string(_condition, _args...);
    }
    void add_modifier(const std::string& _modifier)
    {
      modifiers += _modifier;
    }
    std::string assemble()
    {
      std::string q = "SELECT ";
      q += variables.empty() ? " null " : variables;
      q += tables;
      if(not join_conditions.empty())
      {
        if(table_count == 1)
        {
          q += " WHERE ";
        } else {
          q += " ON ";
        }
        q += join_conditions;
      }
      q += where_conditions;
      q += modifiers;
      return q;
    }
  };

  /**
   * Context for the generation of the sql match query
   */
  struct sql_evaluation_context
  {
    execution_context* exec_c;
    sql_query_builder query_builder;
    std::map<std::string, sql_variable_info> variables;
    std::string previous_table_ref;
  
    // std::string view_name;
    oc::algebra::visitors::expression_analyser expression_analyser;

    table prepare(const table& _table, bool _create_new_table)
    {
      expression_analyser.stage = exception_stage::runtime;
      expression_analyser.variables_f = [this](const std::string& _var_name)
      {
        return get_variable_info(_var_name).type;
      };
      table new_table;
      if(_create_new_table) new_table.view_name = "__view" + std::to_string(++exec_c->next_view_id);
      if(not _table.view_name.empty())
      {
        previous_table_ref = query_builder.add_table(_table.view_name, sql_join::inner, "pt_{}");

        for(auto const& [k, v] : _table.variables)
        {
          std::string sql_var = format_string("{}.{}", previous_table_ref, v.sql_var_name);
          variables[k] = { sql_var, v.type };
          if(_create_new_table)
          {
            std::string col_name = query_builder.add_variable(sql_var);
            new_table.variables[k] = { col_name, v.type };
          }
        }
      }
      return new_table;
    }
    sql_variable_info& get_variable_info(const std::string& _oc_variable)
    {
      auto it = variables.find(_oc_variable);
      if(it == variables.end())
      {
        errors::undefined_variable(exception_stage::runtime, _oc_variable);
      }
      return it->second;
    }
    std::string get_sql_variable(const std::string& _oc_variable)
    {
      return get_variable_info(_oc_variable).sql_var_name;
    }
    std::string get_sql_properties_variable(const std::string& _oc_variable)
    {
      sql_variable_info& info = get_variable_info(_oc_variable);
      std::string properties_var = info.sql_properties_var_name;
      if(properties_var.empty())
      {
        properties_var = retrieve_sql_properties(info.sql_var_name, info.type);
        info.sql_properties_var_name = properties_var;
      }
      return properties_var;
    }
    /**
     * Retrieve SQL properties
     */
    std::string retrieve_sql_properties(const std::string& _sql_var, algebra::expression_type _et)
    {
      std::string table_suffix = (_et == algebra::expression_type::node) ? "nodes" : "edges";
      std::string table_ref = query_builder.add_table(format_string("gqlite_{}_{}", exec_c->graph_name, table_suffix), sql_join::inner, "tbp{}");
      query_builder.add_join_condition("{}.id = {}", table_ref, _sql_var);
      std::string properties_var = format_string("{}.properties", table_ref);
      return properties_var;
    }
  };

  /**
   * Visitor to build a sql query
   */
  struct sql_expression_visitor : public gqlite::oc::algebra::default_node_visitor<std::string>
  {
    sql_evaluation_context* eval_c;
    sql_expression_visitor(sql_evaluation_context* _ec) : eval_c(_ec)
    {}
    std::string visit_default(algebra::node_csp _node) override
    {
      throw_exception(exception_stage::runtime, exception_code::unimplemented_error, "Unimplemented node '{}' in sql_expression_visitor", oc::algebra::node_type_name(_node->get_type()));
    }
    std::string visit(algebra::variable_csp _node) override
    {
      return eval_c->get_sql_variable(_node->get_identifier());
    }
    std::string visit(algebra::end_of_list_csp ) override
    {
      return eval_c->query_builder.bind_value(std::numeric_limits<int64_t>::max());
    }
    std::string visit(algebra::value_csp _node) override
    {
      return eval_c->query_builder.bind_value(_node->get_value());
    }
    std::string visit(algebra::has_labels_csp _node) override
    {
      oc::algebra::expression_type et = eval_c->get_variable_info(_node->get_left()).type;
      std::string label_ids;
      for(const std::string& l : _node->get_labels())
      {
        if(not label_ids.empty()) label_ids += ", ";
        label_ids += std::to_string(eval_c->exec_c->data->id_for_label(l));
      }
      switch(et)
      {
      case oc::algebra::expression_type::node:
        {
          return "(SELECT count(*) == {} FROM gqlite_{}_labels WHERE node_id={} AND label IN ({}) )"s
              % _node->get_labels().size(), eval_c->exec_c->graph_name, eval_c->get_sql_variable(_node->get_left()), label_ids;
        }
      case oc::algebra::expression_type::edge:
        {
          std::string label_id;
          return "(SELECT count(*) > 0 FROM gqlite_{}_edges WHERE id={} AND label IN ({}) )"s
              % eval_c->exec_c->graph_name, eval_c->get_sql_variable(_node->get_left()), label_ids;
        }
      default:
        errors::invalid_argument_type(exception_stage::runtime, "Can only check labels for nodes and edges.");
      }
    }
    std::string visit(algebra::map_csp _map) override
    {
      std::size_t var_uuid = eval_c->query_builder.next_uid();
      std::string map = ("(SELECT json_group_object(name_{}, val_{}) FROM ("s % var_uuid, var_uuid);
      bool first = true;
      for(const auto& [k, v] : _map->get_map())
      {
        if(not first)
        {
          map += " UNION ";
        }
        map += "SELECT " + eval_c->query_builder.bind_value(k);
        if(first)
        {
          map += " AS name_{}"s % var_uuid;
        }
        map += ", " + start(v);
        if(first)
        {
          map += " AS val_{}"s % var_uuid;
        }
        first = false;
      }
      return map + "))";
    }
    std::string visit(algebra::array_csp _array) override
    {
      std::size_t var_uuid = eval_c->query_builder.next_uid();
      std::string array = "(SELECT json_group_array(val_{}) FROM ("s % var_uuid;
      bool first = true;
      for(algebra::node_csp v : _array->get_array())
      {
        if(not first)
        {
          array += " UNION ";
        }
        array += "SELECT " + start(v);
        if(first)
        {
          array += " AS val_{}"s % var_uuid;
        }
        first = false;
      }
      return array + "))";
    }
    std::string visit(algebra::member_access_csp _node) override
    {
      std::string path_prefix;
      algebra::expression_type et = eval_c->expression_analyser.start(_node->get_left()).type;
      std::string expr;
      if(et == algebra::expression_type::node or et == algebra::expression_type::edge)
      {
        errors::check_condition(_node->get_left()->get_type() == algebra::node_type::variable, exception_stage::runtime, exception_code::internal_error, "Only variable are supported yet for getting properties for node/edge.");
        expr = eval_c->get_sql_properties_variable(std::static_pointer_cast<const algebra::variable>(_node->get_left())->get_identifier());
      } else {
        expr = start(_node->get_left());
      }
      return "json_extract({}, '${}.{}')"s % expr, path_prefix, string::join(_node->get_path(), ".");
    }
    std::string visit(algebra::indexed_access_csp _node) override
    {
      algebra::expression_type et = eval_c->expression_analyser.start(_node->get_left()).type;
      std::string expr;
      if(et == algebra::expression_type::node or et == algebra::expression_type::edge)
      {
        errors::check_condition(_node->get_left()->get_type() == algebra::node_type::variable, exception_stage::runtime, exception_code::internal_error, "Only variable are supported yet for getting properties for node/edge.");
        expr = eval_c->get_sql_properties_variable(std::static_pointer_cast<const algebra::variable>(_node->get_left())->get_identifier());
      } else {
        expr = start(_node->get_left());
      }
      if(_node->get_end())
      {
        return "gqlite_range_access({}, {}, {})"s % expr, start(_node->get_index()), start(_node->get_end());
      } else {
        return "gqlite_indexed_access({}, {})"s % expr, start(_node->get_index());
      }
    }
    std::string visit(algebra::function_call_csp _node) override
    {
      if(_node->get_name() == "coalesce")
      {
        std::string arguments;
        for(algebra::node_csp arg : _node->get_arguments())
        {
          if(not arguments.empty()) arguments += ", ";
          arguments += start(arg);
        }
        return "coalesce(" + arguments + ")";
      } else if(_node->get_name() == "labels")
      {
        errors::check_arguments_size("labels", _node->get_arguments(), 1);
        std::string arg = start(_node->get_arguments().front());
        return sqlite_queries::function_labels(eval_c->exec_c->graph_name, arg);
      } else if(_node->get_name() == "type")
      {
        errors::check_arguments_size("type", _node->get_arguments(), 1);
        std::string arg = start(_node->get_arguments().front());
        std::string edges_table_ref = eval_c->query_builder.add_table(format_string("gqlite_{}_edges", eval_c->exec_c->graph_name), sql_join::inner, "tb_edge_{}");
        std::string labels_table_ref = eval_c->query_builder.add_table("gqlite_labels", sql_join::inner, "tb_lab_{}");
        eval_c->query_builder.add_join_condition("{}.id = {} AND {}.label = {}.id", edges_table_ref, arg, edges_table_ref, labels_table_ref);
        return "{}.label"s % labels_table_ref;
      } else if(_node->get_name() == "range")
      {
        std::string args;
        for(algebra::node_csp arg : _node->get_arguments())
        {
          if(not args.empty()) args += ", ";
          args += start(arg);
        }
        return "gqlite_range({})" % args;
      } else if(_node->get_name() == "properties")
      {
        algebra::node_csp arg_0 = _node->get_arguments()[0];
        algebra::expression_type type_0 = eval_c->expression_analyser.start(arg_0).type;
        if(type_0 == algebra::expression_type::map)
        {
          return start(arg_0);
        } else {
          errors::check_condition(arg_0->get_type() == algebra::node_type::variable, exception_stage::runtime, exception_code::internal_error, "Only variable are supported yet for getting properties for node/edge.");
          return eval_c->get_sql_properties_variable(std::static_pointer_cast<const algebra::variable>(arg_0)->get_identifier());
        }
      } else if(_node->get_name() == "keys")
      {
        algebra::node_csp arg_0 = _node->get_arguments()[0];
        algebra::expression_type type_0 = eval_c->expression_analyser.start(arg_0).type;
        std::string map_expr;
        if(type_0 == algebra::expression_type::map)
        {
          map_expr = start(arg_0);
        } else {
          errors::check_condition(arg_0->get_type() == algebra::node_type::variable, exception_stage::runtime, exception_code::internal_error, "Only variable are supported yet for getting properties for node/edge.");
          map_expr = eval_c->get_sql_properties_variable(std::static_pointer_cast<const algebra::variable>(arg_0)->get_identifier());
        }
        return "(SELECT json_group_array(key) FROM json_each({}))" % map_expr;
      } else if(_node->get_name() == "toInteger")
      {
        return "CAST({} AS INTEGER)"s % start(_node->get_arguments()[0]);
      } else if(_node->get_name() == "size")
      {
        return "json_array_length({})"s % start(_node->get_arguments()[0]);
      } else if(_node->get_name() == "id")
      {
        return start(_node->get_arguments()[0]);
      } else if(_node->get_name() == "head")
      {
        return "json_extract({}, '$[0]')" % start(_node->get_arguments()[0]);
      } else if(_node->get_name() == "tail")
      {
        return "gqlite_tail({})" % start(_node->get_arguments()[0]);
      }
      throw_exception(exception_stage::runtime, exception_code::unimplemented_error, "Function call to {} is not implemented.", _node->get_name());
    }
#define FILTER_VISITOR_BINARY_OP(_AL_, _OP_)                                                          \
    std::string visit(algebra::_AL_ ## _csp _node) override                                           \
    {                                                                                                 \
      return "({} " _OP_ " {})"s % start(_node->get_left()), start(_node->get_right());               \
    }
    FILTER_VISITOR_BINARY_OP(logical_and, "AND")
    FILTER_VISITOR_BINARY_OP(logical_or, "OR")
    FILTER_VISITOR_BINARY_OP(logical_xor, "<>")
    FILTER_VISITOR_BINARY_OP(relational_equal, "=")
    FILTER_VISITOR_BINARY_OP(relational_different, "!=")
    FILTER_VISITOR_BINARY_OP(relational_inferior, "<")
    FILTER_VISITOR_BINARY_OP(relational_superior, ">")
    FILTER_VISITOR_BINARY_OP(relational_inferior_equal, "<")
    FILTER_VISITOR_BINARY_OP(relational_superior_equal, ">")
    FILTER_VISITOR_BINARY_OP(substraction, "-")
    FILTER_VISITOR_BINARY_OP(multiplication, "*")
    FILTER_VISITOR_BINARY_OP(division, "/")
    FILTER_VISITOR_BINARY_OP(modulo, "%")

    std::string visit(algebra::relational_in_csp _node) override
    {
      return "gqlite_contains({}, {})" % start(_node->get_right()), start(_node->get_left());
    }
    std::string visit(algebra::relational_not_in_csp _node) override
    {
      return "NOT gqlite_contains({}, {})" % start(_node->get_right()), start(_node->get_left());
    }

    std::string visit(algebra::addition_csp _node) override
    {
      algebra::expression_type left_type = eval_c->expression_analyser.start(_node->get_left()).type;
      algebra::expression_type right_type = eval_c->expression_analyser.start(_node->get_right()).type;

      if(left_type == algebra::expression_type::vector or right_type == algebra::expression_type::vector)
      {
        errors::check_argument_types(left_type, {algebra::expression_type::vector, algebra::expression_type::value}, exception_stage::runtime, "Invalid argument for array concatenation.");

        return "gqlite_concat({}, {})"s % start(_node->get_left()), start(_node->get_right());
      } else if(left_type == algebra::expression_type::string or right_type == algebra::expression_type::string)
      {
        errors::check_argument_types(left_type, {algebra::expression_type::string, algebra::expression_type::value}, exception_stage::runtime, "Invalid argument for string concatenation.");
        errors::check_argument_types(right_type, {algebra::expression_type::string, algebra::expression_type::value}, exception_stage::runtime, "Invalid argument for string concatenation.");

        return "({} || {})"s % start(_node->get_left()), start(_node->get_right());
      } else if(is_number(left_type) or is_number(right_type))
      {
        return "({} + {})"s % start(_node->get_left()), start(_node->get_right());
      } else {
        return "gqlite_addition({}, {})"s % start(_node->get_left()), start(_node->get_right());
      }
    }

#define FILTER_VISITOR_UNARY_OP(_AL_, _OP_)                             \
    std::string visit(algebra::_AL_ ## _csp _node) override             \
    {                                                                   \
      return format_string("(" _OP_ " {})", start(_node->get_value())); \
    }
    FILTER_VISITOR_UNARY_OP(logical_negation, "NOT")
    FILTER_VISITOR_UNARY_OP(negation, "-")
    std::string visit(algebra::is_not_null_csp _node) override
    {
      return format_string("({} IS NOT NULL)", start(_node->get_value()));
    }
    std::string visit(algebra::is_null_csp _node) override
    {
      return format_string("({} IS NULL)", start(_node->get_value()));
    }
  };
  struct sql_match_evaluation_context : public sql_evaluation_context
  {
    std::vector<std::pair<std::string, std::string>> edge_sql_var_to_oc_var;
    /**
     * Generate the SQL join operation needed for matchin labels
     */
    std::string generate_labels_match(const std::vector<std::string>& _labels, const std::string& _node_variable)
    {
      std::string cond;
      for(const std::string& label : _labels)
      {
        std::string tb_lab = query_builder.add_table(format_string("gqlite_{}_labels", exec_c->graph_name), sql_join::inner, "tb_lab{}");
        std::string label_binding = query_builder.bind_value(exec_c->data->id_for_label(label));
        if(not cond.empty()) cond += " AND ";
        cond += format_string(" {} = {}.node_id AND {} = {}.label ", _node_variable, tb_lab, label_binding, tb_lab);
      }
      return cond;
    }
  };
  struct sql_match_expression_visitor : public sql_expression_visitor
  {
    sql_match_evaluation_context* eval_match_c;
    sql_match_expression_visitor(sql_match_evaluation_context* _ec) : sql_expression_visitor(_ec), eval_match_c(_ec)
    {}
    std::string visit_default(algebra::node_csp _node) override
    {
      throw_exception(exception_stage::runtime, exception_code::unimplemented_error, "Unimplemented node '{}' in sql_expression_visitor", oc::algebra::node_type_name(_node->get_type()));
    }
    std::string visit(algebra::has_labels_csp _node) override
    {
      return eval_match_c->generate_labels_match(_node->get_labels(), eval_c->get_sql_variable(_node->get_left()));
    }
  };
  /**
   * Visitor to execute statements: CREATE, MATCHES, RETURN...
   */
  struct statement_visitor : public gqlite::oc::algebra::default_node_visitor<value>
  {
    execution_context exec_c;
    table current_table;
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Common functions
    value visit_default(algebra::node_csp _node) override
    {
      throw_exception(exception_stage::runtime, exception_code::unimplemented_error, "Unimplemented statement node '{}' in statement_visitor", oc::algebra::node_type_name(_node->get_type()));
    }
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Creation functions
    value_vector create_node(algebra::graph_node_csp _node)
    {
      sql_evaluation_context mc;
      mc.exec_c = &exec_c;
      mc.prepare(current_table, false);
      sql_expression_visitor sev{&mc};
      mc.query_builder.add_variable("gqlite_next_uid('nodes')");

      std::string properties_str;
      if(_node->get_properties())
      {
        properties_str = sev.start(_node->get_properties());
      } else {
        properties_str = "'{}'";
      }
      mc.query_builder.add_variable(properties_str);
      std::string query = sqlite_queries::node_create(
        exec_c.graph_name, sev.eval_c->query_builder.assemble());
      value res = exec_c.data->execute_sql(query, sev.eval_c->query_builder.bindings);
      value_vector ret_v;
      value_vector res_v = res.to_vector();
      for(std::size_t i = 0; i < res_v.size(); ++i)
      {
        ret_v.push_back(res_v[i].to_vector()[0]);
      }
      // Add labels
      if(not _node->get_labels().empty())
      {
        value_vector label_ids;
        for(const std::string& label : _node->get_labels())
        {
          label_ids.push_back(exec_c.data->id_for_label(label));
        }
        std::map<int, value> bindings;
        bindings[1] = label_ids;
        bindings[2] = ret_v;
        exec_c.data->execute_sql(sqlite_queries::node_add_label(exec_c.graph_name), bindings);
      }
      return ret_v;
    }
    std::tuple<value, bool, std::string> create_node_if_needed(sql_expression_visitor* _sev, algebra::graph_node_csp _node, const std::map<std::string, value_vector>& _new_nodes, const char* _table_name_format/*, const std::string& _previous_table*/)
    {
      auto it_var = _sev->eval_c->variables.find(_node->get_variable());
      if(it_var == _sev->eval_c->variables.end())
      {
        auto it = _new_nodes.find(_node->get_variable());
        gqlite::value node_values;
        bool new_nodes;
        if(it == _new_nodes.end())
        {
          node_values = create_node(_node);
          new_nodes = true;
        } else {
          node_values = it->second;
          new_nodes = false;
        }
        std::string binding_idx = _sev->eval_c->query_builder.bind_value(node_values);
        std::string table_name_ref = _sev->eval_c->query_builder.add_table(format_string("json_each({})", binding_idx), sql_join::inner, _table_name_format);
        if(not _sev->eval_c->previous_table_ref.empty())
        {
          _sev->eval_c->query_builder.add_join_condition("{}.key + 1 = {}.rowid", table_name_ref, _sev->eval_c->previous_table_ref);
        }
        return std::make_tuple(node_values, new_nodes, format_string("{}.value", table_name_ref));
      } else {
        return std::make_tuple(value(), false, it_var->second.sql_var_name);
      }
    }
    std::tuple<value_vector, value_vector, value_vector> create_edge(algebra::graph_edge_csp _edge, const std::map<std::string, value_vector>& _new_nodes)
    {
      sql_evaluation_context mc;
      mc.exec_c = &exec_c;
      mc.prepare(current_table, false);
      sql_expression_visitor sev{&mc};
      mc.query_builder.add_variable("gqlite_next_uid('edges')");
      // Handle label
      std::string label = _edge->get_labels().empty() ? std::string() : _edge->get_labels().front();
      std::string label_binding_idx = sev.eval_c->query_builder.bind_value(exec_c.data->id_for_label(label));
      mc.query_builder.add_variable(label_binding_idx, "label_{}");

      // Handle properties
      std::string properties_str;
      if(_edge->get_properties())
      {
        properties_str = sev.start(_edge->get_properties());
      } else {
        properties_str = "'{}'";
      }
      mc.query_builder.add_variable(properties_str, "properties_{}");

      // Handle source
      auto const&[left_value, new_left_value, left_expr] = create_node_if_needed(&sev, _edge->get_source(), _new_nodes, "source_node_{}");
      mc.query_builder.add_variable(left_expr, "source_{}");

      // Check if destination == source
      auto const&[right_value, new_right_value, right_expr]
        = (_edge->get_source()->get_variable() == _edge->get_destination()->get_variable()) ?
            std::make_tuple(gqlite::value(), false, left_expr)
          : create_node_if_needed(&sev, _edge->get_destination(), _new_nodes, "destination_node_{}");
      mc.query_builder.add_variable(right_expr, "destination_{}");

      // Execute queries
      std::string query = sqlite_queries::edge_create(exec_c.graph_name, sev.eval_c->query_builder.assemble());
      value res = sev.eval_c->exec_c->data->execute_sql(query, sev.eval_c->query_builder.bindings);

      // Prepare return
      value_vector ret_v;
      value_vector res_v = res.to_vector();
      for(std::size_t i = 0; i < res_v.size(); ++i)
      {
        ret_v.push_back(res_v[i].to_vector()[0]);
      }

      // Check if nodes were created during the edge cretion
      value_vector left_value_ret; if(new_left_value) left_value_ret = left_value.to_vector();
      value_vector right_value_ret; if(new_right_value) right_value_ret = right_value.to_vector();
      return {left_value_ret, ret_v, right_value_ret};
    }
    /**
     * Execute the create statement
     */
    value visit(algebra::create_csp _node) override
    {
      // 1) Create new nodes/edgers
      std::map<std::string, value_vector> new_nodes, new_edges;
      bool new_non_anonymous_nodes = false;
      for(const algebra::alternative<algebra::graph_node, algebra::graph_edge>& pattern : _node->get_patterns())
      {
        pattern.visit<void>([this, &new_nodes, &new_non_anonymous_nodes](const algebra::graph_node_csp _node)
        {
          gqlite::value_vector nodes = create_node(_node);
          if(not _node->get_variable().starts_with("__gqlite_anon_"))
          {
            new_nodes[_node->get_variable()] = nodes;
            new_non_anonymous_nodes = new_non_anonymous_nodes or (not _node->get_variable().starts_with("__gqlite_anon_"));
          }
        },
        [this, &new_nodes, &new_edges, &new_non_anonymous_nodes](const algebra::graph_edge_csp _edge)
        {
          auto const& [left_nodes, edges, right_nodes] = create_edge(_edge, new_nodes);
          if(not left_nodes.empty())
          {
            new_nodes[_edge->get_source()->get_variable()] = left_nodes;
            new_non_anonymous_nodes = new_non_anonymous_nodes or (not _edge->get_source()->get_variable().starts_with("__gqlite_anon_"));
          }
          if(not right_nodes.empty())
          {
            new_nodes[_edge->get_destination()->get_variable()] = right_nodes;
            new_non_anonymous_nodes = new_non_anonymous_nodes or (not _edge->get_destination()->get_variable().starts_with("__gqlite_anon_"));
          }
          new_edges[_edge->get_variable()] = edges;
            new_non_anonymous_nodes = new_non_anonymous_nodes or (not _edge->get_variable().starts_with("__gqlite_anon_"));

        });
      }

      if(new_non_anonymous_nodes)
      {
        // 2) Create the resulting table
        sql_evaluation_context mc;
        mc.exec_c = &exec_c;
        table new_table = mc.prepare(current_table, true);
        sql_expression_visitor sev{&mc};
        std::string first_table_ref;
        // loop over new things
        for(const auto& [new_things, type, var_format]
           : std::initializer_list<std::tuple<std::map<std::string, gqlite::value_vector>, oc::algebra::expression_type, const char*>>
             {{new_nodes, oc::algebra::expression_type::node, "new_node_{}"}, {new_edges, oc::algebra::expression_type::edge, "new_edge_{}"}})
        {
          for(const auto& [k, v] : new_things)
          {
            if(not k.starts_with("__gqlite_anon_"))
            {
              std::string idx = mc.query_builder.bind_value(v);
              std::string table_ref = mc.query_builder.add_table(format_string("json_each({})", idx), sql_join::inner, var_format);
              if(first_table_ref.empty())
              {
                first_table_ref = table_ref;
              } else {
                mc.query_builder.add_join_condition("{}.key = {}.key", first_table_ref, table_ref);
              }
              mc.query_builder.add_variable_with_name(format_string(" {}.value ", table_ref), table_ref);
              new_table.variables[k] = {table_ref, type};
            }
          }
        }
        if(not mc.previous_table_ref.empty())
        {
          mc.query_builder.add_join_condition("{}.rowid = {}.key + 1", mc.previous_table_ref, first_table_ref);
        }
        exec_c.data->execute_sql("CREATE TEMPORARY TABLE " + new_table.view_name + " AS " + mc.query_builder.assemble(), mc.query_builder.bindings);
        current_table = new_table;
      }
      return value{};
    }
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Match
    /**
     * Generate a filter expression for a map
     */
    std::string generate_filter(const algebra::node_csp& _map, const std::string& _path, sql_expression_visitor* _filter_visitor)
    {
      switch(_map->get_type())
      {
        case algebra::node_type::map:
        {
          
          std::string r;
          algebra::map_csp m = std::static_pointer_cast<const algebra::map>(_map);
          for(const auto& [k, v] : m->get_map())
          {
            std::string path = _path + "." + k;
            if(not r.empty())
            {
              r += " AND ";
            }
            if(v->get_type() == algebra::node_type::map)
            {
              r += generate_filter(v, path, _filter_visitor);
            } else {
              r += format_string(" json_extract({}') = {} ", path, _filter_visitor->start(v));
            }
          }
          return r;
        }
        case algebra::node_type::value:
        {
          value val = std::static_pointer_cast<const algebra::value>(_map)->get_value();
          switch(val.get_type())
          {
          case value_type::map:
          {
            // special case for map, as this can't be an equal comparison, since for matching, only a subset of the key might be explicitly defined
            std::string r;
            for(const auto& [k, v] : val.to_map())
            {
              std::string path = _path + "." + k;
              if(not r.empty())
              {
                r += " AND ";
              }
              r += generate_filter(std::make_shared<algebra::value>(v), path, _filter_visitor); // TODO use a seperate function
            }
            return r;
          }
          case value_type::vector:
            {
              std::string id = _filter_visitor->eval_c->query_builder.bind_value(val.to_json());
              return (" json_extract({}') = json({}) "s % _path, id);
            }
          default:
            {
              std::string id = _filter_visitor->eval_c->query_builder.bind_value(val);
              return (" json_extract({}') = {} "s % _path, id);
            }
          }
        }
        default:
          throw_exception(exception_stage::runtime, exception_code::internal_error, "Properties is neither algebra::map nor value_map, this should have been caught by the parser.");
      }
    }
    void generate_var_match(sql_match_evaluation_context* _mc, table* _table, const std::string& _oc_var, const std::string& _sql_var, const std::string& _sql_properties_var, const std::string& _label_var, oc::algebra::expression_type _variable_type)
    {
      if(not _oc_var.empty())
      {
        auto it = _mc->variables.find(_oc_var);
        if(it == _mc->variables.end())
        {
          std::string var_name = _mc->query_builder.add_variable(_sql_var, "new_match_{}");
          _table->variables[_oc_var] = {var_name, _variable_type};
          _mc->variables[_oc_var] = {_sql_var, _variable_type, _sql_properties_var, _label_var};
        } else {
          errors::check_variable_type(it->second.type, _variable_type, exception_stage::runtime, _oc_var);
          // Generate match
          _mc->query_builder.add_join_condition(" {} = {} ", _sql_var, it->second.sql_var_name);
        }
      }
    }
    value visit(algebra::match_csp _node) override
    {
      sql_match_evaluation_context mc;
      mc.exec_c = &exec_c;
      table new_table = mc.prepare(current_table, true);
      sql_match_expression_visitor sev{&mc};

      // 1) Go through the patterns
      for(const algebra::alternative<algebra::graph_node, algebra::graph_edge>& pattern : _node->get_patterns())
      {
        pattern.visit<void>([this, &mc, &sev, &new_table](const algebra::graph_node_csp _node)
        {
          std::string table_ref = sev.eval_c->query_builder.add_table(format_string("gqlite_{}_nodes", exec_c.graph_name), sql_join::inner, "node_{}");
          std::string sql_var = format_string("{}.id", table_ref);
          std::string sql_properties_var = format_string("{}.properties", table_ref);
          mc.query_builder.add_join_condition(mc.generate_labels_match(_node->get_labels(), format_string("{}.id", table_ref)));
          if(_node->get_properties())
          {
            mc.query_builder.add_where_condition(generate_filter(_node->get_properties(), sql_properties_var + ", '$", &sev));
          }
          generate_var_match(&mc, &new_table, _node->get_variable(), sql_var, sql_properties_var, std::string(), algebra::expression_type::node);
        },
        [this, &mc, &sev, &new_table](const algebra::graph_edge_csp _edge)
        {
          std::string table_ref;
          if(_edge->get_directivity() == algebra::edge_directivity::undirected)
          {
            table_ref = sev.eval_c->query_builder.add_table(format_string("gqlite_{}_edges_undirected", exec_c.graph_name), sql_join::inner, "undirected_edge_{}");
          } else {
            table_ref = sev.eval_c->query_builder.add_table(format_string("gqlite_{}_edges", exec_c.graph_name), sql_join::inner, "edge_{}");
          }
          std::string sql_source_var = format_string("{}.left", table_ref);
          std::string sql_edge_var = format_string("{}.id", table_ref);
          std::string sql_properties_edge_var = format_string("{}.properties", table_ref);
          std::string sql_label_edge_var = format_string("{}.label", table_ref);
          std::string sql_source_properties_var;
          std::string sql_destination_properties_var;
          std::string sql_destination_var = format_string("{}.right", table_ref);
          mc.query_builder.add_where_condition(mc.generate_labels_match(_edge->get_source()->get_labels(), table_ref+ ".left"));
          mc.query_builder.add_where_condition(mc.generate_labels_match(_edge->get_destination()->get_labels(), table_ref + ".right"));
          if(not _edge->get_labels().empty())
          {
            std::string label_conds = "(FALSE ";
            for(const std::string& label : _edge->get_labels())
            {
              label_conds += format_string(" OR {} = {}", sql_label_edge_var, exec_c.data->id_for_label(label));
            }
            label_conds += ")";
            mc.query_builder.add_where_condition(label_conds);
          }
          if(_edge->get_properties())
          {
            mc.query_builder.add_where_condition(generate_filter(_edge->get_properties(), sql_properties_edge_var + ", '$", &sev));
          }
          if(_edge->get_source()->get_properties())
          {
            sql_source_properties_var = mc.retrieve_sql_properties(sql_source_var, algebra::expression_type::node);
            mc.query_builder.add_where_condition(generate_filter(_edge->get_source()->get_properties(), sql_source_properties_var + ", '$", &sev));
          }
          if(_edge->get_destination()->get_properties())
          {
            sql_destination_properties_var = mc.retrieve_sql_properties(sql_destination_var, algebra::expression_type::node);
            mc.query_builder.add_where_condition(generate_filter(_edge->get_destination()->get_properties(), sql_destination_properties_var + ", '$", &sev));
          }
          // Ensure identical oc variablle are joined in SQL
          generate_var_match(&mc, &new_table, _edge->get_source()->get_variable(), sql_source_var, sql_source_properties_var, std::string(), algebra::expression_type::node);
          generate_var_match(&mc, &new_table, _edge->get_variable(), sql_edge_var, sql_properties_edge_var, sql_label_edge_var, algebra::expression_type::edge);
          generate_var_match(&mc, &new_table, _edge->get_destination()->get_variable(), sql_destination_var, sql_destination_properties_var, std::string(), algebra::expression_type::node);
          // ensure edge isomorphism, i.e., if the variable names are different, the edges must be different
          for(const std::pair<std::string, std::string>& sql_var_to_oc_var : mc.edge_sql_var_to_oc_var)
          {
            if(sql_var_to_oc_var.second != _edge->get_variable() and not _edge->get_variable().empty())
            {
              mc.query_builder.add_where_condition(" {} != {} ", sql_edge_var, sql_var_to_oc_var.first);
            }
          }
          mc.edge_sql_var_to_oc_var.push_back({sql_edge_var, _edge->get_variable()});
          // Increase variable counter
        });
      }
      // 2) Handle where
      if(_node->get_where())
      {
        mc.query_builder.add_where_condition(sev.start(_node->get_where()));
      }
      // 3) Assemble SQL query for execution
      std::string sql_query = "CREATE TEMPORARY TABLE " + new_table.view_name + " AS " + mc.query_builder.assemble();

      exec_c.data->execute_sql(sql_query, mc.query_builder.bindings);
      current_table = new_table;

      return value{};
    }
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // delete
    value visit(algebra::delete_statement_csp _ds) override
    {
      for(algebra::node_csp node : _ds->get_expressions())
      {
        sql_match_evaluation_context mc;
        mc.exec_c = &exec_c;
        mc.prepare(current_table, false);
        sql_match_expression_visitor sev{&mc};
        std::string expr = sev.start(node);
        mc.query_builder.add_variable(expr);
        std::string what = mc.query_builder.assemble();

        switch(mc.expression_analyser.start(node).type)
        {
          case algebra::expression_type::node:
          {
            if(_ds->get_detach())
            {
              std::string query = sqlite_queries::edge_delete_by_node(exec_c.graph_name, what);
              mc.exec_c->data->execute_sql(query, mc.query_builder.bindings);
            }
            std::string query = sqlite_queries::node_delete(exec_c.graph_name, what);
            mc.exec_c->data->execute_sql(query, mc.query_builder.bindings);
            break;
          }
          case algebra::expression_type::edge:
          {
            std::string query = sqlite_queries::edge_delete(exec_c.graph_name, what);
            mc.exec_c->data->execute_sql(query, mc.query_builder.bindings);
            break;
          }
          default:
            errors::invalid_argument_type(exception_stage::runtime, "Delete expect a node/edge.");
        }
      }
      return value();
      #if 0
      evaluation_context eval_c;
      eval_c.table = table;
      evaluator_visitor eval_v;
      eval_v.exec_c = &exec_c;
      eval_v.eval_c = &eval_c;

      struct deleter
      {
        statement_visitor* self;
        bool detach;
        void operator()(const node_ref_sp& _node)
        {
          if(detach)
          {
            self->exec_c.data->execute_sql(sqlite_queries::edge_delete_by_node(self->exec_c.graph_name), {{1, _node->id}});
          } else {
            value result = self->exec_c.data->execute_sql(sqlite_queries::edge_count_by_node(self->exec_c.graph_name), {{1, _node->id}});
            value_vector rows = result.to_vector();
            errors::check_condition(rows.size() == 1, exception_stage::runtime, exception_code::internal_error, "Invalid number of rows for counting edges got {} expected 1.", rows.size());
            value_vector row = rows.front().to_vector();
            errors::check_condition(row.size() == 1, exception_stage::runtime, exception_code::internal_error, "Invalid number of columns for counting edges got {} expected 1.", rows.size());
            int count = row.front().to_integer();
            errors::check_condition(count == 0, exception_stage::runtime, exception_code::unspecified, "Cannot delete node with {} relationships.", count);
          }
          self->exec_c.data->execute_sql(sqlite_queries::node_delete(self->exec_c.graph_name), {{1, _node->id}});
        }
        void operator()(const edge_ref_sp& _edge)
        {
          self->exec_c.data->execute_sql(sqlite_queries::edge_delete(self->exec_c.graph_name), {{1, _edge->id}});
        }
        void operator()(const value&)
        {
          throw_exception(exception_stage::runtime, exception_code::invalid_argument_type, "Cannot delete a value.");
        }
        void operator()(const empty&)
        {
          throw_exception(exception_stage::runtime, exception_code::invalid_argument_type, "Cannot delete an empty value.");
        }
      };

      for(std::size_t i = 0; i < table.get_rows_count(); ++i)
      {
        value_vector row;
        eval_c.prepare_current_row(table, i, false);
        for(const algebra::node_csp& rv : _ds->get_expressions())
        {
          exec_value ev =eval_v.start(rv);
          std::visit(deleter{this, _ds->get_detach()}, ev);
        }
      }
      return value();
      #endif
    }
    std::string to_sqlite_path(const std::vector<std::string>& _path)
    {
      std::string path_string = "$";
      for(const std::string& pe : _path)
      {
        path_string += "." + pe;
      }
      return path_string;
    }
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // set
    value visit(algebra::set_csp _set) override
    {
      for(algebra::node_csp node : _set->get_nodes())
      {
        struct set_visitor : public gqlite::oc::algebra::default_node_visitor<void>
        {
          statement_visitor* self;
          void visit_default(algebra::node_csp _node) override
          {
            throw_exception(exception_stage::runtime, exception_code::unimplemented_error, "Unimplemented node {} in set_visitor", oc::algebra::node_type_name(_node->get_type()));
          }
          void visit(algebra::set_property_csp _property)
          {
            sql_match_evaluation_context mc;
            mc.exec_c = &self->exec_c;
            mc.prepare(self->current_table, false);
            sql_match_expression_visitor sev{&mc};
            sql_variable_info svi = mc.get_variable_info(_property->get_left());
            // mc.query_builder.add_variable(svi.sql_var_name);
            std::string expr = sev.start(_property->get_expression());
            std::string what = mc.query_builder.assemble().substr(sizeof("SELECT null"));

            switch(svi.type)
            {
            case algebra::expression_type::node:
            {
              std::string query = sqlite_queries::node_set_property(self->exec_c.graph_name, what, expr, self->to_sqlite_path(_property->get_path()), svi.sql_var_name);
              mc.exec_c->data->execute_sql(query, mc.query_builder.bindings);
              break;
            }
            case algebra::expression_type::edge:
            {
              std::string query = sqlite_queries::edge_set_property(self->exec_c.graph_name, what, expr, self->to_sqlite_path(_property->get_path()), svi.sql_var_name);
              mc.exec_c->data->execute_sql(query, mc.query_builder.bindings);
              break;
            }
            default:
              errors::invalid_argument_type(exception_stage::runtime, "Delete expect a node/edge.");
            }
          }
          void visit(algebra::add_property_csp _property)
          {
            sql_match_evaluation_context mc;
            mc.exec_c = &self->exec_c;
            mc.prepare(self->current_table, false);
            sql_match_expression_visitor sev{&mc};
            sql_variable_info svi = mc.get_variable_info(_property->get_left());
            std::string expr = sev.start(_property->get_expression());
            std::string what = mc.query_builder.assemble().substr(sizeof("SELECT null"));

            switch(svi.type)
            {
            case algebra::expression_type::node:
            {
              std::string query = sqlite_queries::node_add_properties(self->exec_c.graph_name, what, expr, self->to_sqlite_path(_property->get_path()), svi.sql_var_name);
              mc.exec_c->data->execute_sql(query, mc.query_builder.bindings);
              break;
            }
            case algebra::expression_type::edge:
            {
              std::string query = sqlite_queries::edge_add_properties(self->exec_c.graph_name, what, expr, self->to_sqlite_path(_property->get_path()), svi.sql_var_name);
              mc.exec_c->data->execute_sql(query, mc.query_builder.bindings);
              break;
            }
            default:
              errors::invalid_argument_type(exception_stage::runtime, "Delete expect a node/edge.");
            }
          }
          void visit(algebra::edit_labels_csp _edit_label)
          {
            sql_match_evaluation_context mc;
            mc.exec_c = &self->exec_c;
            mc.prepare(self->current_table, false);
            sql_match_expression_visitor sev{&mc};
            sql_variable_info svi = mc.get_variable_info(_edit_label->get_target());
            std::vector<std::string> labels = _edit_label->get_labels();
            auto label_ids_view = labels | std::views::transform([this](const std::string& _label) { return value(self->exec_c.data->id_for_label(_label)); });
            std::string labels_ref = mc.query_builder.add_table("json_each({})"s % mc.query_builder.bind_value(value_vector(label_ids_view.begin(), label_ids_view.end())), sql_join::inner);
            mc.query_builder.add_variable("{}.value" % labels_ref);
            mc.query_builder.add_variable(svi.sql_var_name);
            std::string what = mc.query_builder.assemble();

            switch(svi.type)
            {
            case algebra::expression_type::node:
            {
              std::string query = sqlite_queries::node_add_labels(self->exec_c.graph_name, what);
              mc.exec_c->data->execute_sql(query, mc.query_builder.bindings);
              break;
            }
            default:
              errors::invalid_argument_type(exception_stage::runtime, "Can only add label to a node.");
            }
          }
        };
        set_visitor s_v; s_v.self = this;
        s_v.start(node);
      }
      return value();

    }
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // remove
    value visit(algebra::remove_csp _set) override
    {
      for(algebra::node_csp node : _set->get_nodes())
      {
        struct remove_visitor : public gqlite::oc::algebra::default_node_visitor<void>
        {
          statement_visitor* self;
          void visit_default(algebra::node_csp _node) override
          {
            throw_exception(exception_stage::runtime, exception_code::unimplemented_error, "Unimplemented node {} in remove_visitor", oc::algebra::node_type_name(_node->get_type()));
          }
          void visit(algebra::remove_property_csp _property)
          {
            sql_match_evaluation_context mc;
            mc.exec_c = &self->exec_c;
            mc.prepare(self->current_table, false);
            sql_match_expression_visitor sev{&mc};
            sql_variable_info svi = mc.get_variable_info(_property->get_left());
            std::string what = mc.query_builder.assemble().substr(sizeof("SELECT null"));

            switch(svi.type)
            {
            case algebra::expression_type::node:
            {
              std::string query = sqlite_queries::node_remove_property(self->exec_c.graph_name, what, self->to_sqlite_path(_property->get_path()), svi.sql_var_name);
              mc.exec_c->data->execute_sql(query, mc.query_builder.bindings);
              break;
            }
            case algebra::expression_type::edge:
            {
              std::string query = sqlite_queries::edge_remove_property(self->exec_c.graph_name, what, self->to_sqlite_path(_property->get_path()), svi.sql_var_name);
              mc.exec_c->data->execute_sql(query, mc.query_builder.bindings);
              break;
            }
            default:
              errors::invalid_argument_type(exception_stage::runtime, "Delete expect a node/edge.");
            }
          }
          void visit(algebra::edit_labels_csp _edit_label)
          {
            sql_match_evaluation_context mc;
            mc.exec_c = &self->exec_c;
            mc.prepare(self->current_table, false);
            sql_match_expression_visitor sev{&mc};
            sql_variable_info svi = mc.get_variable_info(_edit_label->get_target());
            std::vector<std::string> labels = _edit_label->get_labels();
            auto label_ids_view = labels | std::views::transform([this](const std::string& _label) { return std::to_string(self->exec_c.data->id_for_label(_label)); });
            std::string labels_ref = string::join(label_ids_view, ", ");
            mc.query_builder.add_variable(svi.sql_var_name);
            std::string what = mc.query_builder.assemble();

            switch(svi.type)
            {
            case algebra::expression_type::node:
            {
              std::string query = sqlite_queries::node_remove_label(self->exec_c.graph_name, what, labels_ref);
              mc.exec_c->data->execute_sql(query, mc.query_builder.bindings);
              break;
            }
            default:
              errors::invalid_argument_type(exception_stage::runtime, "Can only add label to a node.");
            }
          }
        };
        remove_visitor s_v; s_v.self = this;
        s_v.start(node);
      }
      return value();
    }
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // add_filter_expressions table
    void add_filter_expressions(sql_expression_visitor* _expr_v, algebra::modifiers_csp _modifiers)
    {
      if(_modifiers)
      {
        if(_modifiers->get_order_by())
        {
          std::string order_by = " ORDER BY ";
          bool first = true;
          for(algebra::order_by_expression_csp node : _modifiers->get_order_by()->get_expressions())
          {
            if(not first) order_by += ", ";
            first = false;
            order_by += _expr_v->start(node->get_expression()) + (node->get_asc() ? " ASC " : " DESC ");
          }
          _expr_v->eval_c->query_builder.add_modifier(order_by);
        }
        if(_modifiers->get_limit())
        {
          _expr_v->eval_c->query_builder.add_modifier(" LIMIT {} " % _expr_v->start(_modifiers->get_limit()));
        }
        if(_modifiers->get_skip())
        {
          if(not _modifiers->get_limit()) _expr_v->eval_c->query_builder.add_modifier(" LIMIT -1 ");
          _expr_v->eval_c->query_builder.add_modifier(" OFFSET {} " % _expr_v->start(_modifiers->get_skip()));
        }
      }
    }
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // with
    value visit(algebra::with_csp w) override
    {
      if(w->get_all() and w->get_expressions().empty())
      {
        return value{};
      }
      if(w->get_all() and not w->get_expressions().empty())
      {
        throw_exception(exception_stage::runtime, exception_code::unspecified, "Unimplemented WITH *, expressions");
      }

      sql_evaluation_context mc;
      mc.exec_c = &exec_c;
      table new_table = mc.prepare(current_table, true);
      new_table.variables.clear();
      sql_expression_visitor sev{&mc};
      // Generate SQL Query
      for(const algebra::named_expression_csp& rv : w->get_expressions())
      {
        std::string var = mc.query_builder.add_variable(sev.start(rv->get_expression()));
        algebra::expression_type et = mc.expression_analyser.start(rv->get_expression()).type; 
        new_table.variables[rv->get_name()] = {var, et};
        mc.variables[rv->get_name()] = {var, et};
      }
      add_filter_expressions(&sev, w->get_modifiers());
      std::string query = "CREATE TEMPORARY TABLE " + new_table.view_name + " AS " + mc.query_builder.assemble();
      exec_c.data->execute_sql(query, mc.query_builder.bindings);

      current_table = new_table;
      return value();
    }
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // unwind
    value visit(algebra::unwind_csp uw) override
    {
      sql_evaluation_context mc;
      mc.exec_c = &exec_c;
      table new_table = mc.prepare(current_table, true);
      sql_expression_visitor sev{&mc};
      std::string table_ref = mc.query_builder.add_table("json_each(" + sev.start(uw->get_expression()) + ")", sql_join::inner, "a_uw_{}");
      std::string var = mc.query_builder.add_variable(format_string("{}.value", table_ref));
      std::string query = "CREATE TEMPORARY TABLE " + new_table.view_name + " AS " + mc.query_builder.assemble();
      new_table.variables[uw->get_name()] = {var, oc::algebra::expression_type::value};
      exec_c.data->execute_sql(query, mc.query_builder.bindings);
      current_table = new_table;
      return value();
    }
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Return
    value visit(algebra::return_statement_csp _rs) override
    {
      sql_evaluation_context mc;
      mc.exec_c = &exec_c;
      mc.prepare(current_table, false);
      sql_expression_visitor sev{&mc};
      std::vector<gqlite::oc::algebra::named_expression_csp> expressions;
      if(_rs->get_all())
      {
        for(const auto& [k, var_info] : current_table.variables)
        {
          if(not k.starts_with("__gqlite_anon_"))
          {
            expressions.push_back(std::make_shared<algebra::named_expression>(k, std::make_shared<algebra::variable>(k)));
          }
        }
      } else {
        expressions = _rs->get_expressions();
      }
      // Generate SQL Query
      value_vector labels;
      std::vector<oc::algebra::expression_type> column_types;
      std::map<std::string, sql_variable_info> variables_extra; /// new variables to set for modifier

      for(const algebra::named_expression_csp& rv : expressions)
      {
        oc::algebra::expression_type et = mc.expression_analyser.start(rv->get_expression()).type;
        switch(et)
        {
        case oc::algebra::expression_type::node:
          {
            std::string table_ref = mc.query_builder.add_table(format_string("gqlite_{}_nodes_as_json", exec_c.graph_name), sql_join::inner, "node_{}");
            mc.query_builder.add_join_condition("{}.id = {}", table_ref, sev.start(rv->get_expression()));
            std::string var_expr = mc.query_builder.add_variable("{}.node" % table_ref);
            variables_extra[rv->get_name()] = {var_expr, oc::algebra::expression_type::node,
                                  "json_extract({}, '$.properties')" % var_expr, "json_extract({}, '$.labels')" % var_expr};
            break;
          }
        case oc::algebra::expression_type::edge:
          {
            std::string table_ref = mc.query_builder.add_table(format_string("gqlite_{}_edges_as_json", exec_c.graph_name), sql_join::inner, "edge_{}");
            mc.query_builder.add_join_condition("{}.id = {}", table_ref, sev.start(rv->get_expression()));
            std::string var_expr = mc.query_builder.add_variable("{}.edge" % table_ref);
            variables_extra[rv->get_name()] = {var_expr, oc::algebra::expression_type::edge,
                                    "json_extract({}, '$.properties')" % var_expr, "json_extract({}, '$.label')" % var_expr};
            break;
          }
        case oc::algebra::expression_type::boolean:
        case oc::algebra::expression_type::vector:
        case oc::algebra::expression_type::map:
          [[fallthrough]];
        default:
          {
            std::string var_expr = mc.query_builder.add_variable(sev.start(rv->get_expression()));
            variables_extra[rv->get_name()] = {var_expr, et};
            break;
          }
        }
        labels.push_back(rv->get_name());
        column_types.push_back(et);
      }
      // Execute SQL query
      if(_rs->get_modifiers())
      {
        mc.variables.insert(variables_extra.begin(), variables_extra.end());
        add_filter_expressions(&sev, _rs->get_modifiers());
      }
      value res = exec_c.data->execute_sql(mc.query_builder.assemble(), mc.query_builder.bindings);
      // Prepare to return results
      value_vector res_v = res.to_vector();
      value_vector ret_v;
      ret_v.push_back(labels);
      for(const value& r : res_v)
      {
        value_vector r_v = r.to_vector();
        for(std::size_t c = 0; c < r_v.size(); ++c)
        {
          value& v = r_v[c];
          switch(column_types[c])
          {
          case oc::algebra::expression_type::boolean:
            if(v.get_type() != value_type::invalid)
            {
              v = (v.to_integer() == 1);
            }
            break;
          case oc::algebra::expression_type::map:
          case oc::algebra::expression_type::vector:
            v = value::from_json(v.to_string());
            break;
          case oc::algebra::expression_type::node:
          case oc::algebra::expression_type::edge:
            v = value::from_json(v.to_string());
            break;
          default:
            if(v.get_type() == value_type::string)
            {
              // Attempt to parse strings as json
              try
              {
                v = value::from_json(v.to_string());
              }
              catch(const exception&)
              {
                // ignore, that probably means it is a real string
              }
              break;
            }
          }
        }
        ret_v.push_back(r_v);
      }
      return ret_v;
    }
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Call
    value visit(algebra::call_csp rs) override
    {
      if(rs->get_arguments().size() != 0) throw_exception(exception_stage::runtime, exception_code::unimplemented_error, "Arguments to call are not supported");
      if(rs->get_yield().size() != 0) throw_exception(exception_stage::runtime, exception_code::unimplemented_error, "Yields to call are not supported");
      auto it = exec_c.data->procedures.find(rs->get_name());
      if(it == exec_c.data->procedures.end())
      {
        throw_exception(exception_stage::runtime, exception_code::unknown_function, "No procedure called {}", rs->get_name());
      }
      return it->second(value_vector());
    }
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Statements
    value visit(algebra::statements_csp _node) override
    {
      for(algebra::node_csp node : _node->get_nodes())
      {
        value val = start(node);
        if(node->get_type() == algebra::node_type::return_statement)
        {
          return val;
        }
      }
      return value{};
    }
  };
}

sqlite::sqlite(void* _db) : d(new data)
{
  d->handle = reinterpret_cast<sqlite3*>(_db);
  if(not d->table_has("gqlite_labels"))
  {
    d->execute_sql(sqlite_queries::label_create_table());
  }
  if(not d->table_has("gqlite_uids"))
  {
    d->execute_sql(sqlite_queries::uid_create_table());
  }
  if(not d->graph_has("default"))
  {
    d->graph_create("default");
  }

  d->procedures["gqlite.internal.stats"] = [this](const value_vector&)
  {
    gqlite::value result = d->execute_sql(sqlite_queries::get_debug_stats("default"));
    value_vector rows = result.to_vector();
    errors::check_condition(rows.size() == 7, exception_stage::runtime, exception_code::internal_error, "Invalid number of debug stats got {} expected 7.", rows.size());
    value_map stats;
    stats["nodes_count"] = rows[0].to_vector()[0].to_integer();
    stats["edges_count"] = rows[1].to_vector()[0].to_integer();
    stats["labels_assignment_count"] = rows[2].to_vector()[0].to_integer();
    stats["properties_count"] = rows[3].to_vector()[0].to_integer() + rows[4].to_vector()[0].to_integer();
    stats["labels_count"] = rows[5].to_vector()[0].to_integer();
    stats["labels_assignment_nodes_count"] = rows[6].to_vector()[0].to_integer();
    return stats;
  };
}

sqlite::~sqlite()
{}

sqlite* sqlite::from_file(const std::string& _filename)
{
  sqlite3* handle;

  if(sqlite3_open(_filename.c_str(), &handle) != SQLITE_OK)
  {
    gqlite_error(sqlite3_errmsg(handle));
    sqlite3_close(handle);
    return nullptr;
  }
  initialise_sqlite_ext(handle);
  return new sqlite(handle);
}

gqlite::value sqlite::execute_oc_query(oc::algebra::node_csp _node)
{
  d->execute_sql("BEGIN");
  try
  {
    sqlite_oc_executor::statement_visitor executor;
    executor.exec_c.data = d;
    value val = executor.start(_node);
    d->execute_sql("COMMIT");
    d->clean_up();
    return val;
  } catch(const exception& _ex)
  {
    d->execute_sql("ROLLBACK");
    d->clean_up();
    rethrow_exception(exception_stage::runtime, _ex);
  }
}
//END backends/sqlite.cpp
//BEGIN backends/sqlite_ext.cpp
#include <sqlite3.h>

#include <algorithm>
#include <limits>
#include <ranges>

#include <gqlite.h>







namespace gqlite::backends
{
  template<typename... _TArgs_>
  void report_error(sqlite3_context* _context, exception_code _code, const std::string& _format, const _TArgs_&... _args)
  {
    value_map err;
    err["code"] = int64_t(_code);
    err["message"] = format_string(_format, _args...);
    std::string s_err = "__gqlite_fun: " + value(err).to_json();
    sqlite3_result_error(_context, s_err.c_str(), s_err.size());
  }
  void report_exception(sqlite3_context* _context, const exception& _ex)
  {
    value_map err;
    err["code"] = int64_t(get_execption_code(_ex));
    err["message"] = get_execption_message(_ex);
    std::string s_err = "__gqlite_fun: " + value(err).to_json();
    sqlite3_result_error(_context, s_err.c_str(), s_err.size());
  }
#define SAFE(_X_)                             \
  try { _X_; }                                \
  catch(const exception& ex)                  \
  {                                           \
    report_exception(_context, ex); return;   \
  }
#define NULL_CHECK(_ARGS_COUNT_)                      \
  for(std::size_t i = 0; i < _ARGS_COUNT_; ++i)       \
  {                                                   \
    if(sqlite3_value_type(_argv[i]) == SQLITE_NULL)   \
    {                                                 \
      sqlite3_result_null(_context); return ;         \
    }                                                 \
  }

  void report_value(sqlite3_context* _context, const value& _val)
  {
    switch(_val.get_type())
    {
      case value_type::boolean:
        sqlite3_result_int(_context, _val.to_bool() ? 1 : 0);
        break;
      case value_type::integer:
        sqlite3_result_int64(_context, _val.to_integer());
        break;
      case value_type::floating_point:
        sqlite3_result_double(_context, _val.to_double());
        break;
      case value_type::string:
      {
        std::string str = _val.to_string();
        sqlite3_result_text(_context, str.c_str(), str.size(), SQLITE_TRANSIENT);
        break;
      }
      case value_type::map:
      case value_type::vector:
      {
        std::string str = _val.to_json();
        sqlite3_result_text(_context, str.c_str(), str.size(), SQLITE_TRANSIENT);
        break;
      }
      case value_type::invalid:
      {
        sqlite3_result_null(_context);
        break;
      }
    }
  }
  enum class comparison_result
  {
    true_result, false_result, compared_null
  };
  comparison_result compare(const gqlite::value& _left, const gqlite::value& _right)
  {
    using enum value_type;
    if(_left.get_type() == invalid  or _right.get_type() == invalid)
    {
      return comparison_result::compared_null;
    }
    if(_left.get_type() != _right.get_type()) return comparison_result::false_result;
    bool comp = false;
    bool compared_to_null = false;
    switch(_left.get_type())
    {
    case invalid:
      break;
    case boolean:
      comp = _left.to_bool() == _right.to_bool();
      break;
    case integer:
      comp = _left.to_integer() == _right.to_integer();
      break;
    case floating_point:
      comp = _left.to_double() == _right.to_double();
      break;
    case string:
      comp = _left.to_string() == _right.to_string();
      break;
    case vector:
      {
        value_vector vv_left = _left.to_vector();
        value_vector vv_right = _right.to_vector();
        if(vv_left.size() != vv_right.size()) return comparison_result::false_result;
        comp = true;
        for(std::size_t idx = 0; idx < vv_left.size(); ++idx)
        {
          const value& l = vv_left[idx];
          const value& r = vv_right[idx];
          comparison_result l_r_cr = compare(l, r);
          // if(l.get_type() == invalid)
          // {
          //   compared_to_null = true;
          // }
          switch(l_r_cr)
          {
          case comparison_result::true_result:
            break;
          case comparison_result::compared_null:
            compared_to_null = true;
            break;
          case comparison_result::false_result:
            comp = false;
            break;
          }
          if(not comp) break;
        }
      }
      break;
    case map:
      comp = _left.to_map() == _right.to_map();
      break;
    }
    // this is opencypher madness, if you compare [nil, 2] with [1, 2] this should return null because of the nil/1 comparison,
    // however [nil, 2] with [1, "2"] should return false because 2 != "2" :facepalm:
    if(not comp) return comparison_result::false_result;
    return (compared_to_null) ? comparison_result::compared_null : comparison_result::true_result;
  }
  comparison_result contains(const gqlite::value_vector& _container, const gqlite::value& _value)
  {
    using enum value_type;
    if(_value.get_type() == invalid)
    {
      return comparison_result::compared_null;
    }
    bool has_compared_to_null = false;
    for(const value& v_c : _container)
    {
      switch(compare(v_c, _value))
      {
      case comparison_result::true_result:
        return comparison_result::true_result;
      case comparison_result::false_result:
        break;
      case comparison_result::compared_null:
        has_compared_to_null = true;
        break;
      }
    }
    return has_compared_to_null ? comparison_result::compared_null : comparison_result::false_result;
  }
  bool contains_null(const gqlite::value_vector& _v)
  {
    for(const value& val : _v)
    {
      if(val.get_type() == value_type::invalid) return true;
    }
    return false;
  }
  /**
   * @internal
   * class use to read json files
   */
  struct is_valid_json
  {
    struct invalid
    {};
    std::stringstream stream;
    int last_char;
    /// @brief get the next char
    void fetch_next_non_space_char()
    {
      while(std::isspace(fetch_next_char()))
      {
        if(last_char == std::stringstream::traits_type::eof())
        {
          return;
        } else if(stream.fail())
        {
          throw invalid();
        }
      }
    };
    int fetch_next_char()
    {
      return (last_char = stream.get());
    }
    /// @brief start parsing
    /// @throw gqlite::exception
    /// @return the parsed value
    bool start()
    {
      try
      {
        fetch_next_non_space_char();
        validate_value();
        return true;
      } catch(const invalid&)
      {
        return false;
      }
    }
    static bool validate(const char* _string)
    {
      is_valid_json ivj{std::stringstream(_string), 0};
      return ivj.start();
    }
    /// @brief read a string
    /// @return the string without quotation
    void validate_string()
    {
      is_char('"');
      fetch_next_char();
      bool keep_nc = false; // this is used to indicate if the last character was a '\' or not
      while(keep_nc or last_char != '"')
      {
        keep_nc = (not keep_nc and last_char == '\\');
        fetch_next_char();
      }
      fetch_next_char(); // eat the '"'
    }
    /// @brief throw an exception if last_char different from @param _c 
    void is_char(int _c)
    {
      if(last_char != _c) throw invalid();
    }
    /// @brief read the next value
    /// @return and return it
    void validate_value()
    {
      switch (last_char)
      {
      case '{':
      {
        // Parse object
        fetch_next_non_space_char();
        while(last_char != '}')
        {
          validate_string();
          is_char(':');
          fetch_next_non_space_char();
          validate_value();
          if(last_char == ',')
          {
            fetch_next_non_space_char();
          } else {
            break;
          }
        }
        is_char('}');
        fetch_next_non_space_char();
        return;
      }
      case '[':
      {
        // Parse array
        fetch_next_non_space_char();
        while(last_char != ']')
        {
          validate_value();
          if(last_char == ',')
          {
            fetch_next_non_space_char();
          } else {
            break;
          }
        }
        is_char(']');
        fetch_next_non_space_char();
        return;
      }
      case '"':
        validate_string();
        return;
      case 't':
      {
        // Parse true
        fetch_next_non_space_char(); is_char('r');
        fetch_next_non_space_char(); is_char('u');
        fetch_next_non_space_char(); is_char('e');
        fetch_next_non_space_char();
        return;
      }
      case 'f':
      {
        // Parse false
        fetch_next_non_space_char(); is_char('a');
        fetch_next_non_space_char(); is_char('l');
        fetch_next_non_space_char(); is_char('s');
        fetch_next_non_space_char(); is_char('e');
        fetch_next_non_space_char();
        return;
      }
      case 'n':
      {
        // Parse null
        fetch_next_non_space_char(); is_char('u');
        fetch_next_non_space_char(); is_char('l');
        fetch_next_non_space_char(); is_char('l');
        fetch_next_non_space_char();
        return;
      }
      // Parse number
      case '-':
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
      {
        fetch_next_non_space_char();
        bool is_integer = true;
        while((last_char >= '0' and last_char <= '9') or last_char == '.' or last_char == 'e' or last_char == '+' or last_char == '-')
        {
          is_integer = is_integer and (last_char != '.' and last_char != 'e');
          fetch_next_non_space_char();
        }
        return;
      }
      default:
        throw invalid{};
      }
    }
  };
  void gqlite_next_uid(sqlite3_context* _context,int,sqlite3_value** _argv)
  {
    // We get our db connection from the context
    sqlite3 *db = sqlite3_context_db_handle(_context);
    const char* label = reinterpret_cast<const char*>(sqlite3_value_text(_argv[0]));
    sqlite3_stmt *ps;
    int rc = sqlite3_prepare_v2(db, sqlite_queries::uid_next().c_str(), -1, &ps, 0);
    if (rc != SQLITE_OK) {
      // Something bad happened, we'll come back to look at this later
      return;
    }
    sqlite3_bind_text(ps, 1, label, -1, SQLITE_STATIC);

    rc = sqlite3_step(ps);
    if (rc == SQLITE_ROW)
    {
      sqlite3_result_int64(_context, sqlite3_column_int64(ps, 0));
    } else {
      report_error(_context, gqlite::exception_code::internal_error, "No uid counter for '{}'.", label);
    }
    sqlite3_finalize(ps);    
  }
  void gqlite_jsonify(sqlite3_context* _context,int,sqlite3_value** _argv)
  {
    constexpr int JSON_TYPE = 74;
    if(sqlite3_value_subtype(_argv[0]) == JSON_TYPE)
    {
      sqlite3_result_subtype(_context, JSON_TYPE);
      sqlite3_result_text(_context, reinterpret_cast<const char*>(sqlite3_value_text(_argv[0])), -1, SQLITE_TRANSIENT);
    } else {
      switch(sqlite3_value_type(_argv[0]))
      {
        case SQLITE_NULL:
          sqlite3_result_null(_context);
          break;
        case SQLITE_INTEGER:
          sqlite3_result_int64(_context, sqlite3_value_int64(_argv[0]));
          break;
        case SQLITE_FLOAT:
          sqlite3_result_double(_context, sqlite3_value_double(_argv[0]));
          break;
        case SQLITE_TEXT:
        {
          const char* str =reinterpret_cast<const char*>(sqlite3_value_text(_argv[0]));
          sqlite3_result_text(_context, str, -1, SQLITE_TRANSIENT);
          if(is_valid_json::validate(str))
          {
            sqlite3_result_subtype(_context, JSON_TYPE);
          }
        }
          break;
        default:
          report_error(_context, exception_code::invalid_argument_type, "Invalid arguments for addition.");
      }
    }
  }
  void gqlite_range(sqlite3_context* _context,int,sqlite3_value** _argv)
  {
    int start = sqlite3_value_int(_argv[0]);
    int end = sqlite3_value_int(_argv[1]);
    value_vector vv;
    for(int64_t i = start; i <= end; ++i)
    {
      vv.push_back(i);
    }
    std::string str = value(vv).to_json().c_str();
    sqlite3_result_text(_context, str.c_str(), str.size(), SQLITE_TRANSIENT);
  }
  void gqlite_concat(sqlite3_context* _context,int,sqlite3_value** _argv)
  {
    std::string left = reinterpret_cast<const char*>(sqlite3_value_text(_argv[0]));
    std::string right = reinterpret_cast<const char*>(sqlite3_value_text(_argv[1]));

    try
    {
      value_vector left_v = value::from_json(left).to_vector();
      value right_v = value::from_json(right);
      if(right_v.get_type() == value_type::vector)
      {
        value_vector right_vv = right_v.to_vector();
        left_v.insert(left_v.end(), right_vv.begin(), right_vv.end());
      } else {
        left_v.push_back(right_v);
      }
        std::string str = value(left_v).to_json();
        sqlite3_result_text(_context, str.c_str(), str.size(), SQLITE_TRANSIENT);
    } catch(const exception& _ex)
    {
      report_error(_context, exception_code::unspecified, _ex.what());
    }
  }
  void gqlite_addition(sqlite3_context* _context,int,sqlite3_value** _argv)
  {
    int left_type = sqlite3_value_type(_argv[0]);
    int right_type = sqlite3_value_type(_argv[1]);
    if(left_type == right_type)
    {
      switch(left_type)
      {
        case SQLITE_INTEGER:
          sqlite3_result_int64(_context, sqlite3_value_int64(_argv[0]) + sqlite3_value_int64(_argv[1]));
          break;
        case SQLITE_FLOAT:
          sqlite3_result_double(_context, sqlite3_value_double(_argv[0]) + sqlite3_value_double(_argv[1]));
          break;
        case SQLITE_TEXT:
        {
          std::string left_s(reinterpret_cast<const char*>(sqlite3_value_text(_argv[0])));
          std::string right_s(reinterpret_cast<const char*>(sqlite3_value_text(_argv[1])));
          std::string res = left_s + right_s;
          sqlite3_result_text(_context, res.c_str(), res.size(), SQLITE_TRANSIENT);
        }
          break;
        default:
          report_error(_context, exception_code::invalid_argument_type, "Invalid arguments for addition.");
      }
    } else {
      double left_v = 0.0, right_v = 0.0;
      switch(left_type)
      {
        case SQLITE_INTEGER:
          left_v = sqlite3_value_int64(_argv[0]);
          break;
        case SQLITE_FLOAT:
          left_v = sqlite3_value_double(_argv[0]);
          break;
        default:
          report_error(_context, exception_code::invalid_argument_type, "Inavlid left argument for addition.");
          return;
      }
      switch(right_type)
      {
        case SQLITE_INTEGER:
          right_v = sqlite3_value_int64(_argv[1]);
          break;
        case SQLITE_FLOAT:
          right_v = sqlite3_value_double(_argv[1]);
          break;
        default:
          report_error(_context, exception_code::invalid_argument_type, "Inavlid right argument for addition.");
          return;
      }
      sqlite3_result_double(_context, left_v + right_v);
    }
  }
  void gqlite_indexed_access(sqlite3_context* _context,int,sqlite3_value** _argv)
  {
    std::string left = reinterpret_cast<const char*>(sqlite3_value_text(_argv[0]));
    value val = value::from_json(left);
    switch(val.get_type())
    {
      case value_type::vector:
      {
        std::size_t idx = sqlite3_value_int(_argv[1]);
        value_vector vv = val.to_vector();
        if(idx >= vv.size())
        {
          report_error(_context, exception_code::unspecified, "Index out of bound.");
          return;
        }
        val = vv[idx];
        break;
      }
      case value_type::map:
      {
        std::string key = reinterpret_cast<const char*>(sqlite3_value_text(_argv[1]));
        val = val.to_map()[key];
        break;
      }
      default:
      {
        report_error(_context, exception_code::invalid_argument_type, "Invalid index access to non map/vector value.");
        return;
      }
    }
    report_value(_context, val);
  }
  void gqlite_range_access(sqlite3_context* _context,int,sqlite3_value** _argv)
  {
    std::string left = reinterpret_cast<const char*>(sqlite3_value_text(_argv[0]));
    value val;
    SAFE(val = value::from_json(left));
    switch(val.get_type())
    {
      case value_type::vector:
      {
        if(sqlite3_value_type(_argv[2]) == SQLITE_NULL or sqlite3_value_type(_argv[1]) == SQLITE_NULL)
        {
          val = value();
        } else {
          value_vector vv = val.to_vector();
          int64_t idx_first = sqlite3_value_int64(_argv[1]);
          int64_t idx_second = sqlite3_value_int64(_argv[2]);
          int64_t v_size = vv.size();
          if(idx_first <= idx_second and idx_first < v_size)
          {
            if(idx_second == std::numeric_limits<int64_t>::max())
            {
              idx_second = vv.size();
            }
            if(idx_first < 0)
            {
              idx_first = 0;
            }
            if(idx_second > v_size)
            {
              idx_second = v_size;
            }
            while(idx_second < 0)
            {
              idx_second += v_size;
            }
            value_vector vv_out;
            for(int64_t i = idx_first; i < idx_second; ++i)
            {
              vv_out.push_back(vv[i]);
            }
            val = vv_out;
          } else {
            val = value_vector();
          }
        }
        break;
      }
      default:
      {
        report_error(_context, exception_code::invalid_argument_type, "Invalid range access to non vector value.");
        return;
      }
    }
    report_value(_context, val);
  }
  void gqlite_contains(sqlite3_context* _context,int,sqlite3_value** _argv)
  {
    NULL_CHECK(2);
    std::string left = reinterpret_cast<const char*>(sqlite3_value_text(_argv[0]));
    value val_left;
    SAFE(val_left = value::from_json(left));
    bool contains_result = false;
    switch(val_left.get_type())
    {
      case value_type::vector:
      {
        std::string right = reinterpret_cast<const char*>(sqlite3_value_text(_argv[1]));
        value_vector vv = val_left.to_vector();
        value val_right;
        SAFE(val_right = value::from_json(right));

        switch(contains(vv, val_right))
        {
        case comparison_result::true_result:
          contains_result = true;
          break;
        case comparison_result::false_result:
          contains_result = false;
          break;
        case comparison_result::compared_null:
          sqlite3_result_null(_context);
          return;
        }
        break;
      }
      case value_type::map:
      {
        std::string key = reinterpret_cast<const char*>(sqlite3_value_text(_argv[1]));
        value_map map = val_left.to_map();
        auto kv = workarounds::views::keys(map);
        contains_result = std::ranges::find(kv, key) != kv.end();
        break;
      }
      default:
      {
        report_error(_context, exception_code::invalid_argument_type, "Invalid IN operator on non map/vector value.");
        return;
      }
    }
    sqlite3_result_int(_context, contains_result ? 1 : 0);
  }
  void gqlite_tail(sqlite3_context* _context,int,sqlite3_value** _argv)
  {
    NULL_CHECK(1);
    std::string left = reinterpret_cast<const char*>(sqlite3_value_text(_argv[0]));
    value val_left;
    SAFE(val_left = value::from_json(left));
    if(val_left.get_type() == value_type::vector)
    {
      value_vector vv = val_left.to_vector();
      if(vv.empty())
      {
        sqlite3_result_null(_context);
      } else {
        report_value(_context, value_vector(std::next(vv.begin()), vv.end()));
      }
    } else {
      report_error(_context, exception_code::invalid_argument_type, "Invalid argument to function tail, list was expected.");
    }
  }

  void initialise_sqlite_ext(sqlite3* db)
  {
    sqlite3_create_function(db, "gqlite_jsonify", 1, SQLITE_UTF8, nullptr, &gqlite_jsonify, nullptr, nullptr);
    sqlite3_create_function(db, "gqlite_next_uid", 1, SQLITE_UTF8, nullptr, &gqlite_next_uid, nullptr, nullptr);
    sqlite3_create_function(db, "gqlite_range", 2, SQLITE_DETERMINISTIC | SQLITE_UTF8, nullptr, &gqlite_range, nullptr, nullptr);
    sqlite3_create_function(db, "gqlite_concat", 2, SQLITE_DETERMINISTIC | SQLITE_UTF8, nullptr, &gqlite_concat, nullptr, nullptr);
    sqlite3_create_function(db, "gqlite_addition", 2, SQLITE_DETERMINISTIC | SQLITE_UTF8, nullptr, &gqlite_addition, nullptr, nullptr);
    sqlite3_create_function(db, "gqlite_indexed_access", 2, SQLITE_DETERMINISTIC | SQLITE_UTF8, nullptr, &gqlite_indexed_access, nullptr, nullptr);
    sqlite3_create_function(db, "gqlite_range_access", 3, SQLITE_DETERMINISTIC | SQLITE_UTF8, nullptr, &gqlite_range_access, nullptr, nullptr);
    sqlite3_create_function(db, "gqlite_contains", 2, SQLITE_DETERMINISTIC | SQLITE_UTF8, nullptr, &gqlite_contains, nullptr, nullptr);
    sqlite3_create_function(db, "gqlite_tail", 1, SQLITE_DETERMINISTIC | SQLITE_UTF8, nullptr, &gqlite_tail, nullptr, nullptr);
  }
}//END backends/sqlite_ext.cpp
