gqlite 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/ext/gqlite/gqlite-amalgamate.cpp +816 -307
  3. metadata +2 -2
@@ -68,6 +68,11 @@ namespace gqlite
68
68
  return _v;
69
69
  }
70
70
  template<>
71
+ inline std::string to_string<std::string_view>(const std::string_view& _v)
72
+ {
73
+ return std::string(_v);
74
+ }
75
+ template<>
71
76
  inline std::string to_string<const char*>(const char* const& _v)
72
77
  {
73
78
  return _v;
@@ -249,7 +254,9 @@ namespace gqlite
249
254
  unimplemented_error,
250
255
  // OpenCypher error code
251
256
  column_name_conflict = 100,
257
+ delete_connected_node,
252
258
  integer_overflow,
259
+ invalid_aggregation,
253
260
  invalid_argument_type,
254
261
  invalid_delete,
255
262
  invalid_number_literal,
@@ -523,12 +530,13 @@ namespace gqlite::oc::algebra
523
530
  {
524
531
  enum class expression_type
525
532
  {
526
- empty, value, boolean, integer, floating_point, string, map, vector, node, edge, path
533
+ empty, value, boolean, integer, floating_point, string, map, vector, node, edge, path, all
527
534
  };
528
535
  struct expression_info
529
536
  {
530
537
  expression_type type = expression_type::empty;
531
538
  bool constant = true;
539
+ bool aggregation_result;
532
540
  };
533
541
  }
534
542
  //END oc/algebra/expression_info.h
@@ -669,6 +677,7 @@ TOKEN_KEYWORD(NOT)
669
677
  TOKEN_KEYWORD(MATCH)
670
678
  TOKEN_KEYWORD2(NULL_CAPS, "NULL")
671
679
  TOKEN_KEYWORD2(NULL_TOKEN, "null")
680
+ TOKEN_KEYWORD(OPTIONAL)
672
681
  TOKEN_KEYWORD(OR)
673
682
  TOKEN_KEYWORD(ORDER)
674
683
  TOKEN_KEYWORD(REMOVE)
@@ -775,7 +784,8 @@ OC_ALGEBRA_GENERATE(create, OC_ALGEBRA_CREATE_NODES_MEMBERS)
775
784
 
776
785
  #define OC_ALGEBRA_MATCH_NODES_MEMBERS(_KLASS_NAME_, F) \
777
786
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns) \
778
- F(_KLASS_NAME_, node_csp, where)
787
+ F(_KLASS_NAME_, node_csp, where) \
788
+ F(_KLASS_NAME_, bool, optional)
779
789
 
780
790
  OC_ALGEBRA_GENERATE(match, OC_ALGEBRA_MATCH_NODES_MEMBERS)
781
791
 
@@ -877,6 +887,7 @@ OC_ALGEBRA_GENERATE(edit_labels, OC_ALGEBRA_EDIT_LABELS_MEMBERS)
877
887
 
878
888
  #define OC_ALGEBRA_NO_MEMBERS(_KLASS_NAME_, F)
879
889
 
890
+ OC_ALGEBRA_GENERATE(all, OC_ALGEBRA_NO_MEMBERS)
880
891
  OC_ALGEBRA_GENERATE(end_of_list, OC_ALGEBRA_NO_MEMBERS)
881
892
 
882
893
  #define OC_ALGEBRA_GRAPH_NODE_MEMBERS(_KLASS_NAME_, F) \
@@ -901,7 +912,6 @@ OC_ALGEBRA_GENERATE(graph_edge, OC_ALGEBRA_GRAPH_EDGE_MEMBERS)
901
912
 
902
913
  OC_ALGEBRA_GENERATE(value, OC_ALGEBRA_VALUE_MEMBERS)
903
914
 
904
-
905
915
  #define OC_ALGEBRA_MAP_MEMBERS(_KLASS_NAME_, F) \
906
916
  F(_KLASS_NAME_, std::unordered_map<GQLITE_LIST(std::string, node_csp)>, map)
907
917
 
@@ -1133,7 +1143,8 @@ OC_ALGEBRA_GENERATE(create, OC_ALGEBRA_CREATE_NODES_MEMBERS)
1133
1143
 
1134
1144
  #define OC_ALGEBRA_MATCH_NODES_MEMBERS(_KLASS_NAME_, F) \
1135
1145
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns) \
1136
- F(_KLASS_NAME_, node_csp, where)
1146
+ F(_KLASS_NAME_, node_csp, where) \
1147
+ F(_KLASS_NAME_, bool, optional)
1137
1148
 
1138
1149
  OC_ALGEBRA_GENERATE(match, OC_ALGEBRA_MATCH_NODES_MEMBERS)
1139
1150
 
@@ -1235,6 +1246,7 @@ OC_ALGEBRA_GENERATE(edit_labels, OC_ALGEBRA_EDIT_LABELS_MEMBERS)
1235
1246
 
1236
1247
  #define OC_ALGEBRA_NO_MEMBERS(_KLASS_NAME_, F)
1237
1248
 
1249
+ OC_ALGEBRA_GENERATE(all, OC_ALGEBRA_NO_MEMBERS)
1238
1250
  OC_ALGEBRA_GENERATE(end_of_list, OC_ALGEBRA_NO_MEMBERS)
1239
1251
 
1240
1252
  #define OC_ALGEBRA_GRAPH_NODE_MEMBERS(_KLASS_NAME_, F) \
@@ -1259,7 +1271,6 @@ OC_ALGEBRA_GENERATE(graph_edge, OC_ALGEBRA_GRAPH_EDGE_MEMBERS)
1259
1271
 
1260
1272
  OC_ALGEBRA_GENERATE(value, OC_ALGEBRA_VALUE_MEMBERS)
1261
1273
 
1262
-
1263
1274
  #define OC_ALGEBRA_MAP_MEMBERS(_KLASS_NAME_, F) \
1264
1275
  F(_KLASS_NAME_, std::unordered_map<GQLITE_LIST(std::string, node_csp)>, map)
1265
1276
 
@@ -1376,7 +1387,8 @@ OC_ALGEBRA_GENERATE(create, OC_ALGEBRA_CREATE_NODES_MEMBERS)
1376
1387
 
1377
1388
  #define OC_ALGEBRA_MATCH_NODES_MEMBERS(_KLASS_NAME_, F) \
1378
1389
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns) \
1379
- F(_KLASS_NAME_, node_csp, where)
1390
+ F(_KLASS_NAME_, node_csp, where) \
1391
+ F(_KLASS_NAME_, bool, optional)
1380
1392
 
1381
1393
  OC_ALGEBRA_GENERATE(match, OC_ALGEBRA_MATCH_NODES_MEMBERS)
1382
1394
 
@@ -1478,6 +1490,7 @@ OC_ALGEBRA_GENERATE(edit_labels, OC_ALGEBRA_EDIT_LABELS_MEMBERS)
1478
1490
 
1479
1491
  #define OC_ALGEBRA_NO_MEMBERS(_KLASS_NAME_, F)
1480
1492
 
1493
+ OC_ALGEBRA_GENERATE(all, OC_ALGEBRA_NO_MEMBERS)
1481
1494
  OC_ALGEBRA_GENERATE(end_of_list, OC_ALGEBRA_NO_MEMBERS)
1482
1495
 
1483
1496
  #define OC_ALGEBRA_GRAPH_NODE_MEMBERS(_KLASS_NAME_, F) \
@@ -1502,7 +1515,6 @@ OC_ALGEBRA_GENERATE(graph_edge, OC_ALGEBRA_GRAPH_EDGE_MEMBERS)
1502
1515
 
1503
1516
  OC_ALGEBRA_GENERATE(value, OC_ALGEBRA_VALUE_MEMBERS)
1504
1517
 
1505
-
1506
1518
  #define OC_ALGEBRA_MAP_MEMBERS(_KLASS_NAME_, F) \
1507
1519
  F(_KLASS_NAME_, std::unordered_map<GQLITE_LIST(std::string, node_csp)>, map)
1508
1520
 
@@ -1618,7 +1630,8 @@ OC_ALGEBRA_GENERATE(create, OC_ALGEBRA_CREATE_NODES_MEMBERS)
1618
1630
 
1619
1631
  #define OC_ALGEBRA_MATCH_NODES_MEMBERS(_KLASS_NAME_, F) \
1620
1632
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns) \
1621
- F(_KLASS_NAME_, node_csp, where)
1633
+ F(_KLASS_NAME_, node_csp, where) \
1634
+ F(_KLASS_NAME_, bool, optional)
1622
1635
 
1623
1636
  OC_ALGEBRA_GENERATE(match, OC_ALGEBRA_MATCH_NODES_MEMBERS)
1624
1637
 
@@ -1720,6 +1733,7 @@ OC_ALGEBRA_GENERATE(edit_labels, OC_ALGEBRA_EDIT_LABELS_MEMBERS)
1720
1733
 
1721
1734
  #define OC_ALGEBRA_NO_MEMBERS(_KLASS_NAME_, F)
1722
1735
 
1736
+ OC_ALGEBRA_GENERATE(all, OC_ALGEBRA_NO_MEMBERS)
1723
1737
  OC_ALGEBRA_GENERATE(end_of_list, OC_ALGEBRA_NO_MEMBERS)
1724
1738
 
1725
1739
  #define OC_ALGEBRA_GRAPH_NODE_MEMBERS(_KLASS_NAME_, F) \
@@ -1744,7 +1758,6 @@ OC_ALGEBRA_GENERATE(graph_edge, OC_ALGEBRA_GRAPH_EDGE_MEMBERS)
1744
1758
 
1745
1759
  OC_ALGEBRA_GENERATE(value, OC_ALGEBRA_VALUE_MEMBERS)
1746
1760
 
1747
-
1748
1761
  #define OC_ALGEBRA_MAP_MEMBERS(_KLASS_NAME_, F) \
1749
1762
  F(_KLASS_NAME_, std::unordered_map<GQLITE_LIST(std::string, node_csp)>, map)
1750
1763
 
@@ -1877,7 +1890,8 @@ OC_ALGEBRA_GENERATE(create, OC_ALGEBRA_CREATE_NODES_MEMBERS)
1877
1890
 
1878
1891
  #define OC_ALGEBRA_MATCH_NODES_MEMBERS(_KLASS_NAME_, F) \
1879
1892
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns) \
1880
- F(_KLASS_NAME_, node_csp, where)
1893
+ F(_KLASS_NAME_, node_csp, where) \
1894
+ F(_KLASS_NAME_, bool, optional)
1881
1895
 
1882
1896
  OC_ALGEBRA_GENERATE(match, OC_ALGEBRA_MATCH_NODES_MEMBERS)
1883
1897
 
@@ -1979,6 +1993,7 @@ OC_ALGEBRA_GENERATE(edit_labels, OC_ALGEBRA_EDIT_LABELS_MEMBERS)
1979
1993
 
1980
1994
  #define OC_ALGEBRA_NO_MEMBERS(_KLASS_NAME_, F)
1981
1995
 
1996
+ OC_ALGEBRA_GENERATE(all, OC_ALGEBRA_NO_MEMBERS)
1982
1997
  OC_ALGEBRA_GENERATE(end_of_list, OC_ALGEBRA_NO_MEMBERS)
1983
1998
 
1984
1999
  #define OC_ALGEBRA_GRAPH_NODE_MEMBERS(_KLASS_NAME_, F) \
@@ -2003,7 +2018,6 @@ OC_ALGEBRA_GENERATE(graph_edge, OC_ALGEBRA_GRAPH_EDGE_MEMBERS)
2003
2018
 
2004
2019
  OC_ALGEBRA_GENERATE(value, OC_ALGEBRA_VALUE_MEMBERS)
2005
2020
 
2006
-
2007
2021
  #define OC_ALGEBRA_MAP_MEMBERS(_KLASS_NAME_, F) \
2008
2022
  F(_KLASS_NAME_, std::unordered_map<GQLITE_LIST(std::string, node_csp)>, map)
2009
2023
 
@@ -2137,7 +2151,8 @@ OC_ALGEBRA_GENERATE(create, OC_ALGEBRA_CREATE_NODES_MEMBERS)
2137
2151
 
2138
2152
  #define OC_ALGEBRA_MATCH_NODES_MEMBERS(_KLASS_NAME_, F) \
2139
2153
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns) \
2140
- F(_KLASS_NAME_, node_csp, where)
2154
+ F(_KLASS_NAME_, node_csp, where) \
2155
+ F(_KLASS_NAME_, bool, optional)
2141
2156
 
2142
2157
  OC_ALGEBRA_GENERATE(match, OC_ALGEBRA_MATCH_NODES_MEMBERS)
2143
2158
 
@@ -2239,6 +2254,7 @@ OC_ALGEBRA_GENERATE(edit_labels, OC_ALGEBRA_EDIT_LABELS_MEMBERS)
2239
2254
 
2240
2255
  #define OC_ALGEBRA_NO_MEMBERS(_KLASS_NAME_, F)
2241
2256
 
2257
+ OC_ALGEBRA_GENERATE(all, OC_ALGEBRA_NO_MEMBERS)
2242
2258
  OC_ALGEBRA_GENERATE(end_of_list, OC_ALGEBRA_NO_MEMBERS)
2243
2259
 
2244
2260
  #define OC_ALGEBRA_GRAPH_NODE_MEMBERS(_KLASS_NAME_, F) \
@@ -2263,7 +2279,6 @@ OC_ALGEBRA_GENERATE(graph_edge, OC_ALGEBRA_GRAPH_EDGE_MEMBERS)
2263
2279
 
2264
2280
  OC_ALGEBRA_GENERATE(value, OC_ALGEBRA_VALUE_MEMBERS)
2265
2281
 
2266
-
2267
2282
  #define OC_ALGEBRA_MAP_MEMBERS(_KLASS_NAME_, F) \
2268
2283
  F(_KLASS_NAME_, std::unordered_map<GQLITE_LIST(std::string, node_csp)>, map)
2269
2284
 
@@ -2400,7 +2415,8 @@ OC_ALGEBRA_GENERATE(create, OC_ALGEBRA_CREATE_NODES_MEMBERS)
2400
2415
 
2401
2416
  #define OC_ALGEBRA_MATCH_NODES_MEMBERS(_KLASS_NAME_, F) \
2402
2417
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns) \
2403
- F(_KLASS_NAME_, node_csp, where)
2418
+ F(_KLASS_NAME_, node_csp, where) \
2419
+ F(_KLASS_NAME_, bool, optional)
2404
2420
 
2405
2421
  OC_ALGEBRA_GENERATE(match, OC_ALGEBRA_MATCH_NODES_MEMBERS)
2406
2422
 
@@ -2502,6 +2518,7 @@ OC_ALGEBRA_GENERATE(edit_labels, OC_ALGEBRA_EDIT_LABELS_MEMBERS)
2502
2518
 
2503
2519
  #define OC_ALGEBRA_NO_MEMBERS(_KLASS_NAME_, F)
2504
2520
 
2521
+ OC_ALGEBRA_GENERATE(all, OC_ALGEBRA_NO_MEMBERS)
2505
2522
  OC_ALGEBRA_GENERATE(end_of_list, OC_ALGEBRA_NO_MEMBERS)
2506
2523
 
2507
2524
  #define OC_ALGEBRA_GRAPH_NODE_MEMBERS(_KLASS_NAME_, F) \
@@ -2526,7 +2543,6 @@ OC_ALGEBRA_GENERATE(graph_edge, OC_ALGEBRA_GRAPH_EDGE_MEMBERS)
2526
2543
 
2527
2544
  OC_ALGEBRA_GENERATE(value, OC_ALGEBRA_VALUE_MEMBERS)
2528
2545
 
2529
-
2530
2546
  #define OC_ALGEBRA_MAP_MEMBERS(_KLASS_NAME_, F) \
2531
2547
  F(_KLASS_NAME_, std::unordered_map<GQLITE_LIST(std::string, node_csp)>, map)
2532
2548
 
@@ -2639,19 +2655,19 @@ namespace gqlite::oc::algebra::visitors
2639
2655
  switch(_var->get_value().get_type())
2640
2656
  {
2641
2657
  case value_type::invalid:
2642
- return {expression_type::empty, true };
2658
+ return {expression_type::empty, true, false};
2643
2659
  case value_type::boolean:
2644
- return {expression_type::boolean, true };
2660
+ return {expression_type::boolean, true, false};
2645
2661
  case value_type::integer:
2646
- return {expression_type::integer, true };
2662
+ return {expression_type::integer, true , false};
2647
2663
  case value_type::floating_point:
2648
- return {expression_type::floating_point, true };
2664
+ return {expression_type::floating_point, true, false};
2649
2665
  case value_type::string:
2650
- return {expression_type::string, true };
2666
+ return {expression_type::string, true, false};
2651
2667
  case value_type::map:
2652
- return {expression_type::map, true };
2668
+ return {expression_type::map, true, false};
2653
2669
  case value_type::vector:
2654
- return {expression_type::vector, true };
2670
+ return {expression_type::vector, true, false};
2655
2671
  };
2656
2672
  throw_exception(exception_stage::unspecified, exception_code::internal_error, "Unknown value type.");
2657
2673
  }
@@ -2667,32 +2683,50 @@ namespace gqlite::oc::algebra::visitors
2667
2683
  }
2668
2684
  return true;
2669
2685
  }
2686
+ template<typename _T_>
2687
+ bool aggregation_result_nodes(const _T_& _nodes)
2688
+ {
2689
+ for(algebra::node_csp n : _nodes)
2690
+ {
2691
+ if(start(n).aggregation_result)
2692
+ {
2693
+ return true;
2694
+ }
2695
+ }
2696
+ return false;
2697
+ }
2670
2698
  expression_info visit(algebra::array_csp _value) override
2671
2699
  {
2672
- return {expression_type::vector, constant_nodes(_value->get_array())};
2700
+ return {expression_type::vector, constant_nodes(_value->get_array()), aggregation_result_nodes(_value->get_array())};
2673
2701
  }
2674
2702
  expression_info visit(algebra::map_csp _value) override
2675
2703
  {
2676
- return {expression_type::map, constant_nodes(workarounds::views::values(_value->get_map()))};
2704
+ return {expression_type::map, constant_nodes(workarounds::views::values(_value->get_map())), aggregation_result_nodes(workarounds::views::values(_value->get_map()))};
2677
2705
  }
2678
2706
  expression_info visit(algebra::member_access_csp _ma) override
2679
2707
  {
2680
- return {expression_type::value, start(_ma->get_left()).constant};
2708
+ return {expression_type::value, start(_ma->get_left()).constant, false};
2709
+ }
2710
+ expression_info visit(algebra::all_csp) override
2711
+ {
2712
+ return {expression_type::all, false, false};
2681
2713
  }
2682
2714
  expression_info visit(algebra::indexed_access_csp _ia) override
2683
2715
  {
2684
- return {expression_type::value, start(_ia->get_left()).constant};
2716
+ return {expression_type::value, start(_ia->get_left()).constant, false};
2685
2717
  }
2686
2718
  expression_info visit(algebra::has_labels_csp) override
2687
2719
  {
2688
- return {expression_type::boolean, false};
2720
+ return {expression_type::boolean, false, false};
2689
2721
  }
2690
- #define GQLITE_ET_LOGICAL_OP(_NAME_) \
2691
- expression_info visit(algebra::_NAME_ ## _csp _node) override \
2692
- { \
2693
- bool constant = start(_node->get_left()).constant \
2694
- and start(_node->get_right()).constant; \
2695
- return {expression_type::boolean, constant}; \
2722
+ #define GQLITE_ET_LOGICAL_OP(_NAME_) \
2723
+ expression_info visit(algebra::_NAME_ ## _csp _node) override \
2724
+ { \
2725
+ expression_info left = start(_node->get_left()); \
2726
+ expression_info right = start(_node->get_right()); \
2727
+ bool constant = left.constant and right.constant; \
2728
+ bool aggregation_result = left.aggregation_result or right.aggregation_result; \
2729
+ return {expression_type::boolean, constant, aggregation_result}; \
2696
2730
  }
2697
2731
  GQLITE_ET_LOGICAL_OP(logical_and)
2698
2732
  GQLITE_ET_LOGICAL_OP(logical_or)
@@ -2705,11 +2739,11 @@ namespace gqlite::oc::algebra::visitors
2705
2739
  GQLITE_ET_LOGICAL_OP(relational_superior_equal)
2706
2740
  GQLITE_ET_LOGICAL_OP(relational_in)
2707
2741
  GQLITE_ET_LOGICAL_OP(relational_not_in)
2708
- #define GQLITE_ET_LOGICAL_UNARY_OP(_NAME_) \
2709
- expression_info visit(algebra::_NAME_ ## _csp _node) override \
2710
- { \
2711
- bool constant = start(_node->get_value()).constant; \
2712
- return {expression_type::boolean, constant}; \
2742
+ #define GQLITE_ET_LOGICAL_UNARY_OP(_NAME_) \
2743
+ expression_info visit(algebra::_NAME_ ## _csp _node) override \
2744
+ { \
2745
+ expression_info info = start(_node->get_value()); \
2746
+ return {expression_type::boolean, info.constant, info.aggregation_result}; \
2713
2747
  }
2714
2748
  GQLITE_ET_LOGICAL_UNARY_OP(is_null)
2715
2749
  GQLITE_ET_LOGICAL_UNARY_OP(is_not_null)
@@ -2719,21 +2753,22 @@ namespace gqlite::oc::algebra::visitors
2719
2753
  expression_info left = start(_node->get_left()); \
2720
2754
  expression_info right = start(_node->get_right()); \
2721
2755
  bool constant = left.constant and right.constant; \
2722
- if(left.type == right.type) return {left.type, constant}; \
2756
+ bool aggregation_result = left.aggregation_result or right.aggregation_result; \
2757
+ if(left.type == right.type) return {left.type, constant, aggregation_result}; \
2723
2758
  if(left.type == expression_type::value or right.type == expression_type::value) \
2724
- return {expression_type::value, constant}; \
2759
+ return {expression_type::value, constant, aggregation_result}; \
2725
2760
  if(left.type == expression_type::floating_point or right.type == expression_type::floating_point) \
2726
2761
  { \
2727
- return {expression_type::floating_point, constant}; \
2762
+ return {expression_type::floating_point, constant, aggregation_result}; \
2728
2763
  } \
2729
2764
  if(left.type == expression_type::string or right.type == expression_type::string) \
2730
2765
  { \
2731
- return {expression_type::string, constant}; \
2766
+ return {expression_type::string, constant, aggregation_result}; \
2732
2767
  } \
2733
2768
  if(std::is_same_v<algebra::_NAME_ ## _csp, algebra::addition_csp> \
2734
2769
  and left.type == expression_type::vector) \
2735
2770
  { \
2736
- return {expression_type::vector, constant}; \
2771
+ return {expression_type::vector, constant, aggregation_result}; \
2737
2772
  } \
2738
2773
  errors::invalid_argument_type(stage, "In binary operation."); \
2739
2774
  }
@@ -2745,18 +2780,19 @@ namespace gqlite::oc::algebra::visitors
2745
2780
 
2746
2781
  expression_info visit(algebra::logical_negation_csp _node)
2747
2782
  {
2748
- bool constant = start(_node->get_value()).constant;
2749
- return {expression_type::boolean, constant};
2783
+ expression_info ei = start(_node->get_value());
2784
+ return {expression_type::boolean, ei.constant, ei.aggregation_result};
2750
2785
  }
2751
2786
  expression_info visit(algebra::variable_csp _var) override
2752
2787
  {
2753
- return {variables_f(_var->get_identifier()), false};
2788
+ return {variables_f(_var->get_identifier()), false, false};
2754
2789
  }
2755
2790
  struct function_info
2756
2791
  {
2757
2792
  expression_type return_type;
2758
2793
  std::vector<expression_type> arguments_types;
2759
2794
  bool deterministic;
2795
+ bool aggregation;
2760
2796
  };
2761
2797
  static std::unordered_multimap<std::string, function_info> create_functions()
2762
2798
  {
@@ -2764,29 +2800,52 @@ namespace gqlite::oc::algebra::visitors
2764
2800
  using enum expression_type;
2765
2801
  using FI = function_info;
2766
2802
  using V = std::vector<expression_type>;
2767
- f.emplace("head", FI{value, V{vector}, true});
2768
- f.emplace("id", FI{integer, V{node}, true});
2769
- f.emplace("id", FI{integer, V{edge}, true});
2770
- f.emplace("keys", FI{vector, V{edge}, true});
2771
- f.emplace("keys", FI{vector, V{node}, true});
2772
- f.emplace("keys", FI{vector, V{map}, true});
2773
- f.emplace("labels", FI{vector, V{node}, true});
2774
- f.emplace("properties", FI{map, V{node}, true});
2775
- f.emplace("properties", FI{map, V{edge}, true});
2776
- f.emplace("properties", FI{map, V{map}, true});
2777
- f.emplace("range", FI{string, V{integer, integer}, true});
2778
- f.emplace("size", FI{integer, V{vector}, true});
2779
- f.emplace("tail", FI{vector, V{vector}, true});
2780
- f.emplace("toInteger", FI{integer, V{integer}, true});
2781
- f.emplace("toInteger", FI{integer, V{floating_point}, true});
2782
- f.emplace("type", FI{string, V{edge}, true});
2803
+ f.emplace("head", FI{value, V{vector}, true, false});
2804
+ f.emplace("id", FI{integer, V{node}, true, false});
2805
+ f.emplace("id", FI{integer, V{edge}, true, false});
2806
+ f.emplace("keys", FI{vector, V{edge}, true, false});
2807
+ f.emplace("keys", FI{vector, V{node}, true, false});
2808
+ f.emplace("keys", FI{vector, V{map}, true, false});
2809
+ f.emplace("labels", FI{vector, V{node}, true, false});
2810
+ f.emplace("properties", FI{map, V{node}, true, false});
2811
+ f.emplace("properties", FI{map, V{edge}, true, false});
2812
+ f.emplace("properties", FI{map, V{map}, true, false});
2813
+ f.emplace("range", FI{string, V{integer, integer}, true, false});
2814
+ f.emplace("size", FI{integer, V{vector}, true, false});
2815
+ f.emplace("tail", FI{vector, V{vector}, true, false});
2816
+ f.emplace("toInteger", FI{integer, V{integer}, true, false});
2817
+ f.emplace("toInteger", FI{integer, V{floating_point}, true, false});
2818
+ f.emplace("type", FI{string, V{edge}, true, false});
2819
+ // aggregations
2820
+ f.emplace("avg", FI{integer, V{value}, true, true});
2821
+ f.emplace("count", FI{integer, V{value}, true, true});
2822
+ f.emplace("count", FI{integer, V{all}, true, true});
2823
+ f.emplace("collect", FI{vector, V{value}, true, true});
2824
+ f.emplace("max", FI{value, V{value}, true, true});
2825
+ f.emplace("min", FI{value, V{value}, true, true});
2826
+ f.emplace("sum", FI{value, V{value}, true, true});
2783
2827
  return f;
2784
2828
  }
2785
2829
  expression_info visit(algebra::function_call_csp _node) override
2786
2830
  {
2787
2831
  if(_node->get_name() == "coalesce")
2788
2832
  { // coalesce is a special case that can accept any value
2789
- return {expression_type::value, constant_nodes(_node->get_arguments())};
2833
+ expression_type et = expression_type::empty;
2834
+ bool first = true;
2835
+ for(const algebra::node_csp& n : _node->get_arguments())
2836
+ {
2837
+ expression_info ei = start(n);
2838
+ if(first)
2839
+ {
2840
+ first = false;
2841
+ et = ei.type;
2842
+ } else if(et != ei.type)
2843
+ {
2844
+ et = expression_type::value;
2845
+ break;
2846
+ }
2847
+ }
2848
+ return {et, constant_nodes(_node->get_arguments()), aggregation_result_nodes(_node->get_arguments())};
2790
2849
  }
2791
2850
  // 1) Check if all arguments are constant and get their type
2792
2851
  bool constant_arguments = true;
@@ -2815,7 +2874,7 @@ namespace gqlite::oc::algebra::visitors
2815
2874
  }
2816
2875
  break;
2817
2876
  }
2818
- if(match) return {it->second.return_type, it->second.deterministic and constant_arguments};
2877
+ if(match) return {it->second.return_type, it->second.deterministic and constant_arguments, it->second.aggregation};
2819
2878
  }
2820
2879
  }
2821
2880
  // 3) If no function was found, return an exception for unknown function or invalid arguments
@@ -2876,6 +2935,10 @@ namespace gqlite::oc::algebra::visitors
2876
2935
  std::string visit(algebra::value_csp _var) override
2877
2936
  {
2878
2937
  return _var->get_value().to_json();
2938
+ }
2939
+ std::string visit(algebra::all_csp) override
2940
+ {
2941
+ return "*";
2879
2942
  }
2880
2943
  std::string visit(algebra::array_csp _var) override
2881
2944
  {
@@ -2922,9 +2985,16 @@ namespace gqlite::oc::algebra::visitors
2922
2985
  #define GQLITE_STRINGIFY_UNARY_OP(_NAME_, _OP_) \
2923
2986
  std::string visit(algebra::_NAME_ ## _csp _var) override \
2924
2987
  { \
2925
- return # _OP_ " " + start(_var->get_value()); \
2988
+ return # _OP_ " " + start(_var->get_value()); \
2926
2989
  }
2927
2990
  GQLITE_STRINGIFY_UNARY_OP(logical_negation, NOT)
2991
+ #define GQLITE_STRINGIFY_UNARY_END_OP(_NAME_, _OP_) \
2992
+ std::string visit(algebra::_NAME_ ## _csp _var) override \
2993
+ { \
2994
+ return start(_var->get_value()) + " " # _OP_; \
2995
+ }
2996
+ GQLITE_STRINGIFY_UNARY_END_OP(is_null, IS NULL)
2997
+ GQLITE_STRINGIFY_UNARY_END_OP(is_not_null, IS NOT NULL)
2928
2998
  std::string visit(algebra::variable_csp _var) override
2929
2999
  {
2930
3000
  return _var->get_identifier();
@@ -2984,7 +3054,7 @@ namespace gqlite
2984
3054
 
2985
3055
  namespace gqlite::backends::sqlite_queries
2986
3056
  {
2987
- 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)
3057
+ 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)
2988
3058
  {
2989
3059
  std::stringstream stream;
2990
3060
 
@@ -2996,11 +3066,11 @@ stream << "_edges AS tblu SET properties=json_patch(tblu.properties, json_set('{
2996
3066
 
2997
3067
  stream << ( _path );
2998
3068
 
2999
- stream << "\", ";
3069
+ stream << "\", gqlite_jsonify(";
3000
3070
 
3001
3071
  stream << ( _expr );
3002
3072
 
3003
- stream << ")) ";
3073
+ stream << "))) ";
3004
3074
 
3005
3075
  stream << ( _what );
3006
3076
 
@@ -3010,19 +3080,24 @@ stream << ( _edge_id );
3010
3080
 
3011
3081
  return stream.str();
3012
3082
  }
3013
- std::string edge_count_by_node(const std::string& _graph_name)
3083
+ inline std::string edge_count_by_nodes(const std::string& _graph_name, const std::string& _what)
3014
3084
  {
3015
3085
  std::stringstream stream;
3016
3086
 
3017
- stream << "SELECT count(*) FROM gqlite_";
3087
+ stream << "WITH source_delete AS NOT MATERIALIZED (";
3088
+
3089
+ stream << ( _what );
3090
+
3091
+ stream << ")\n"
3092
+ "SELECT count(*) FROM gqlite_";
3018
3093
 
3019
3094
  stream << ( _graph_name );
3020
3095
 
3021
- stream << "_edges WHERE left=?001 or right=?001";
3096
+ stream << "_edges WHERE left IN source_delete or right IN source_delete";
3022
3097
 
3023
3098
  return stream.str();
3024
3099
  }
3025
- std::string edge_create(const std::string& _graph_name, const std::string& _values)
3100
+ inline std::string edge_create(const std::string& _graph_name, const std::string& _values)
3026
3101
  {
3027
3102
  std::stringstream stream;
3028
3103
 
@@ -3030,7 +3105,7 @@ stream << "INSERT INTO gqlite_";
3030
3105
 
3031
3106
  stream << ( _graph_name );
3032
3107
 
3033
- stream << "_edges (label, properties, left, right) ";
3108
+ stream << "_edges (id, label, properties, left, right) ";
3034
3109
 
3035
3110
  stream << ( _values );
3036
3111
 
@@ -3040,7 +3115,7 @@ stream << "\n"
3040
3115
 
3041
3116
  return stream.str();
3042
3117
  }
3043
- std::string edge_delete(const std::string& _graph_name, const std::string& _what)
3118
+ inline std::string edge_delete(const std::string& _graph_name, const std::string& _what)
3044
3119
  {
3045
3120
  std::stringstream stream;
3046
3121
 
@@ -3056,7 +3131,7 @@ stream << ")";
3056
3131
 
3057
3132
  return stream.str();
3058
3133
  }
3059
- std::string edge_delete_by_node(const std::string& _graph_name, const std::string& _what)
3134
+ inline std::string edge_delete_by_nodes(const std::string& _graph_name, const std::string& _what)
3060
3135
  {
3061
3136
  std::stringstream stream;
3062
3137
 
@@ -3073,7 +3148,7 @@ stream << "_edges WHERE left IN source_delete or right IN source_delete";
3073
3148
 
3074
3149
  return stream.str();
3075
3150
  }
3076
- std::string edge_get_label_properties(const std::string& _graph_name)
3151
+ inline std::string edge_get_label_properties(const std::string& _graph_name)
3077
3152
  {
3078
3153
  std::stringstream stream;
3079
3154
 
@@ -3085,7 +3160,7 @@ stream << "_edges WHERE id = ?001";
3085
3160
 
3086
3161
  return stream.str();
3087
3162
  }
3088
- 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)
3163
+ 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)
3089
3164
  {
3090
3165
  std::stringstream stream;
3091
3166
 
@@ -3097,11 +3172,11 @@ stream << "_edges AS tblu SET properties=json_patch('{}', json_set(tblu.properti
3097
3172
 
3098
3173
  stream << ( _path );
3099
3174
 
3100
- stream << "\", ";
3175
+ stream << "\", gqlite_jsonify(";
3101
3176
 
3102
3177
  stream << ( _expr );
3103
3178
 
3104
- stream << ")) ";
3179
+ stream << "))) ";
3105
3180
 
3106
3181
  stream << ( _what );
3107
3182
 
@@ -3111,7 +3186,7 @@ stream << ( _edge_id );
3111
3186
 
3112
3187
  return stream.str();
3113
3188
  }
3114
- std::string edge_remove_property(const std::string& _graph_name, const std::string& _what, const std::string& _path, const std::string& _edge_id)
3189
+ inline std::string edge_remove_property(const std::string& _graph_name, const std::string& _what, const std::string& _path, const std::string& _edge_id)
3115
3190
  {
3116
3191
  std::stringstream stream;
3117
3192
 
@@ -3133,26 +3208,34 @@ stream << ( _edge_id );
3133
3208
 
3134
3209
  return stream.str();
3135
3210
  }
3136
- std::string function_labels(const std::string& _graph_name, const std::string& _node_id)
3211
+ inline std::string function_labels(const std::string& _graph_name, const std::string& _node_id)
3137
3212
  {
3138
3213
  std::stringstream stream;
3139
3214
 
3140
- stream << "(SELECT json_group_array(labels.label)\n"
3141
- " FROM gqlite_";
3215
+ stream << "(CASE WHEN ";
3216
+
3217
+ stream << ( _node_id );
3218
+
3219
+ stream << " IS NULL\n"
3220
+ " THEN NULL\n"
3221
+ " ELSE\n"
3222
+ " (SELECT json_group_array(labels.label)\n"
3223
+ " FROM gqlite_";
3142
3224
 
3143
3225
  stream << ( _graph_name );
3144
3226
 
3145
3227
  stream << "_labels as l\n"
3146
- " JOIN gqlite_labels AS labels ON labels.id = l.label WHERE l.node_id = ";
3228
+ " JOIN gqlite_labels AS labels ON labels.id = l.label WHERE l.node_id = ";
3147
3229
 
3148
3230
  stream << ( _node_id );
3149
3231
 
3150
3232
  stream << ")\n"
3233
+ " END)\n"
3151
3234
  "";
3152
3235
 
3153
3236
  return stream.str();
3154
3237
  }
3155
- std::string get_debug_stats(const std::string& _graph_name)
3238
+ inline std::string get_debug_stats(const std::string& _graph_name)
3156
3239
  {
3157
3240
  std::stringstream stream;
3158
3241
 
@@ -3186,11 +3269,26 @@ stream << "_edges t, json_each(properties) j\n"
3186
3269
 
3187
3270
  stream << ( _graph_name );
3188
3271
 
3189
- stream << "_labels";
3272
+ stream << "_labels\n"
3273
+ "UNION ALL SELECT COUNT(DISTINCT label) FROM (SELECT label FROM gqlite_";
3274
+
3275
+ stream << ( _graph_name );
3276
+
3277
+ stream << "_edges UNION SELECT label FROM gqlite_";
3278
+
3279
+ stream << ( _graph_name );
3280
+
3281
+ stream << "_labels)\n"
3282
+ "UNION ALL SELECT COUNT(DISTINCT label) FROM gqlite_";
3283
+
3284
+ stream << ( _graph_name );
3285
+
3286
+ stream << "_labels\n"
3287
+ "";
3190
3288
 
3191
3289
  return stream.str();
3192
3290
  }
3193
- std::string graph_create(const std::string& _graph_name)
3291
+ inline std::string graph_create(const std::string& _graph_name)
3194
3292
  {
3195
3293
  std::stringstream stream;
3196
3294
 
@@ -3269,7 +3367,8 @@ stream << ( _graph_name );
3269
3367
 
3270
3368
  stream << "_labels as l ON l.node_id = n.id\n"
3271
3369
  " LEFT JOIN gqlite_labels AS labels ON labels.id = l.label\n"
3272
- " GROUP BY n.id);\n"
3370
+ " GROUP BY n.id)\n"
3371
+ " UNION SELECT NULL, NULL;\n"
3273
3372
  "\n"
3274
3373
  "CREATE VIEW gqlite_";
3275
3374
 
@@ -3285,12 +3384,12 @@ stream << ( _graph_name );
3285
3384
 
3286
3385
  stream << "_edges e\n"
3287
3386
  " JOIN gqlite_labels label ON label.id = e.label\n"
3288
- "\n"
3387
+ " UNION SELECT NULL, NULL\n"
3289
3388
  "";
3290
3389
 
3291
3390
  return stream.str();
3292
3391
  }
3293
- std::string graph_has(const std::string& _graph_name)
3392
+ inline std::string graph_has(const std::string& _graph_name)
3294
3393
  {
3295
3394
  std::stringstream stream;
3296
3395
 
@@ -3312,7 +3411,7 @@ stream << "_labels')";
3312
3411
 
3313
3412
  return stream.str();
3314
3413
  }
3315
- std::string label_create_table()
3414
+ inline std::string label_create_table()
3316
3415
  {
3317
3416
  std::stringstream stream;
3318
3417
 
@@ -3321,7 +3420,7 @@ stream << "CREATE TABLE gqlite_labels(id INTEGER PRIMARY KEY AUTOINCREMENT, labe
3321
3420
 
3322
3421
  return stream.str();
3323
3422
  }
3324
- std::string label_get_from_id()
3423
+ inline std::string label_get_from_id()
3325
3424
  {
3326
3425
  std::stringstream stream;
3327
3426
 
@@ -3330,7 +3429,7 @@ stream << "SELECT label FROM gqlite_labels WHERE id = ?001\n"
3330
3429
 
3331
3430
  return stream.str();
3332
3431
  }
3333
- std::string label_get_from_name()
3432
+ inline std::string label_get_from_name()
3334
3433
  {
3335
3434
  std::stringstream stream;
3336
3435
 
@@ -3339,7 +3438,7 @@ stream << "SELECT id FROM gqlite_labels WHERE label = ?001\n"
3339
3438
 
3340
3439
  return stream.str();
3341
3440
  }
3342
- std::string label_insert()
3441
+ inline std::string label_insert()
3343
3442
  {
3344
3443
  std::stringstream stream;
3345
3444
 
@@ -3347,7 +3446,7 @@ stream << "INSERT INTO gqlite_labels(label) VALUES (?001)";
3347
3446
 
3348
3447
  return stream.str();
3349
3448
  }
3350
- std::string node_add_label(const std::string& _graph_name)
3449
+ inline std::string node_add_label(const std::string& _graph_name)
3351
3450
  {
3352
3451
  std::stringstream stream;
3353
3452
 
@@ -3355,11 +3454,11 @@ stream << "INSERT INTO gqlite_";
3355
3454
 
3356
3455
  stream << ( _graph_name );
3357
3456
 
3358
- stream << "_labels(label, node_id) SELECT label.value, node.value FROM json_each(?001) label JOIN json_each(?002) node";
3457
+ stream << "_labels(label, node_id) SELECT label.value, node.value FROM json_each(?001) label JOIN json_each(?002) node WHERE node.value IS NOT NULL";
3359
3458
 
3360
3459
  return stream.str();
3361
3460
  }
3362
- std::string node_add_labels(const std::string& _graph_name, const std::string& _what)
3461
+ inline std::string node_add_labels(const std::string& _graph_name, const std::string& _what)
3363
3462
  {
3364
3463
  std::stringstream stream;
3365
3464
 
@@ -3376,7 +3475,7 @@ stream << "\n"
3376
3475
 
3377
3476
  return stream.str();
3378
3477
  }
3379
- 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)
3478
+ 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)
3380
3479
  {
3381
3480
  std::stringstream stream;
3382
3481
 
@@ -3388,11 +3487,11 @@ stream << "_nodes AS tblu SET properties=json_patch(tblu.properties, json_set('{
3388
3487
 
3389
3488
  stream << ( _path );
3390
3489
 
3391
- stream << "\", ";
3490
+ stream << "\", gqlite_jsonify(";
3392
3491
 
3393
3492
  stream << ( _expr );
3394
3493
 
3395
- stream << ")) ";
3494
+ stream << "))) ";
3396
3495
 
3397
3496
  stream << ( _what );
3398
3497
 
@@ -3402,7 +3501,7 @@ stream << ( _node_id );
3402
3501
 
3403
3502
  return stream.str();
3404
3503
  }
3405
- std::string node_create(const std::string& _graph_name, const std::string& _values)
3504
+ inline std::string node_create(const std::string& _graph_name, const std::string& _values)
3406
3505
  {
3407
3506
  std::stringstream stream;
3408
3507
 
@@ -3410,7 +3509,7 @@ stream << "INSERT INTO gqlite_";
3410
3509
 
3411
3510
  stream << ( _graph_name );
3412
3511
 
3413
- stream << "_nodes (properties) ";
3512
+ stream << "_nodes (id, properties) ";
3414
3513
 
3415
3514
  stream << ( _values );
3416
3515
 
@@ -3419,7 +3518,7 @@ stream << "\n"
3419
3518
 
3420
3519
  return stream.str();
3421
3520
  }
3422
- std::string node_delete(const std::string& _graph_name, const std::string& _what)
3521
+ inline std::string node_delete(const std::string& _graph_name, const std::string& _what)
3423
3522
  {
3424
3523
  std::stringstream stream;
3425
3524
 
@@ -3444,7 +3543,7 @@ stream << ")";
3444
3543
 
3445
3544
  return stream.str();
3446
3545
  }
3447
- std::string node_get_labels(const std::string& _graph_name)
3546
+ inline std::string node_get_labels(const std::string& _graph_name)
3448
3547
  {
3449
3548
  std::stringstream stream;
3450
3549
 
@@ -3456,7 +3555,7 @@ stream << "_labels WHERE node_id = ?001";
3456
3555
 
3457
3556
  return stream.str();
3458
3557
  }
3459
- std::string node_get_properties(const std::string& _graph_name)
3558
+ inline std::string node_get_properties(const std::string& _graph_name)
3460
3559
  {
3461
3560
  std::stringstream stream;
3462
3561
 
@@ -3468,7 +3567,7 @@ stream << "_nodes WHERE id = ?001";
3468
3567
 
3469
3568
  return stream.str();
3470
3569
  }
3471
- std::string node_remove_label(const std::string& _graph_name, const std::string& _what, const std::string& _labels)
3570
+ inline std::string node_remove_label(const std::string& _graph_name, const std::string& _what, const std::string& _labels)
3472
3571
  {
3473
3572
  std::stringstream stream;
3474
3573
 
@@ -3489,7 +3588,7 @@ stream << ")\n"
3489
3588
 
3490
3589
  return stream.str();
3491
3590
  }
3492
- std::string node_remove_property(const std::string& _graph_name, const std::string& _what, const std::string& _path, const std::string& _node_id)
3591
+ inline std::string node_remove_property(const std::string& _graph_name, const std::string& _what, const std::string& _path, const std::string& _node_id)
3493
3592
  {
3494
3593
  std::stringstream stream;
3495
3594
 
@@ -3511,7 +3610,7 @@ stream << ( _node_id );
3511
3610
 
3512
3611
  return stream.str();
3513
3612
  }
3514
- 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)
3613
+ 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)
3515
3614
  {
3516
3615
  std::stringstream stream;
3517
3616
 
@@ -3523,11 +3622,11 @@ stream << "_nodes AS tblu SET properties=json_patch('{}', json_set(tblu.properti
3523
3622
 
3524
3623
  stream << ( _path );
3525
3624
 
3526
- stream << "\", ";
3625
+ stream << "\", gqlite_jsonify(";
3527
3626
 
3528
3627
  stream << ( _expr );
3529
3628
 
3530
- stream << ")) ";
3629
+ stream << "))) ";
3531
3630
 
3532
3631
  stream << ( _what );
3533
3632
 
@@ -3537,7 +3636,7 @@ stream << ( _node_id );
3537
3636
 
3538
3637
  return stream.str();
3539
3638
  }
3540
- std::string node_select_many(const std::string& _graph_name, int _idx)
3639
+ inline std::string node_select_many(const std::string& _graph_name, int _idx)
3541
3640
  {
3542
3641
  std::stringstream stream;
3543
3642
 
@@ -3556,12 +3655,32 @@ stream << ") s ON s.value = nas.id\n"
3556
3655
 
3557
3656
  return stream.str();
3558
3657
  }
3559
- std::string table_has()
3658
+ inline std::string table_has()
3560
3659
  {
3561
3660
  std::stringstream stream;
3562
3661
 
3563
3662
  stream << "SELECT count(*) FROM sqlite_master WHERE type='table' AND (name=?001)";
3564
3663
 
3664
+ return stream.str();
3665
+ }
3666
+ inline std::string uid_create_table()
3667
+ {
3668
+ std::stringstream stream;
3669
+
3670
+ stream << "CREATE TABLE gqlite_uids(name TEXT PRIMARY KEY, value INTEGER NOT NULL);\n"
3671
+ "INSERT INTO gqlite_uids VALUES ('nodes', 0);\n"
3672
+ "INSERT INTO gqlite_uids VALUES ('edges', 0)\n"
3673
+ "";
3674
+
3675
+ return stream.str();
3676
+ }
3677
+ inline std::string uid_next()
3678
+ {
3679
+ std::stringstream stream;
3680
+
3681
+ stream << "UPDATE gqlite_uids SET value=value+1 WHERE name=?001\n"
3682
+ "RETURNING value";
3683
+
3565
3684
  return stream.str();
3566
3685
  }
3567
3686
  }
@@ -3646,6 +3765,11 @@ connection connection::create_from_sqlite_file(const std::string& _filename, con
3646
3765
 
3647
3766
  value connection::execute_oc_query(const std::string& _string, const value_map& _bindings)
3648
3767
  {
3768
+ #if 0
3769
+ std::cout << "============= execute_oc_query =============\n"
3770
+ << _string <<
3771
+ "\n============================================" << std::endl;
3772
+ #endif
3649
3773
  std::stringstream ss(_string);
3650
3774
  oc::lexer l(&ss);
3651
3775
  oc::parser parser(&l, _bindings);
@@ -3776,8 +3900,12 @@ const char* exception::what() const throw()
3776
3900
  d->full_error += "UnimplementedError: "; break;
3777
3901
  case column_name_conflict:
3778
3902
  d->full_error += "ColumnNameConflict: "; break;
3903
+ case delete_connected_node:
3904
+ d->full_error += "DeleteConnectedNode: "; break;
3779
3905
  case integer_overflow:
3780
3906
  d->full_error += "IntegerOverflow: "; break;
3907
+ case invalid_aggregation:
3908
+ d->full_error += "InvalidAggregation: "; break;
3781
3909
  case invalid_argument_type:
3782
3910
  d->full_error += "InvalidArgumentType: "; break;
3783
3911
  case invalid_delete:
@@ -4549,6 +4677,7 @@ TOKEN_KEYWORD(NOT)
4549
4677
  TOKEN_KEYWORD(MATCH)
4550
4678
  TOKEN_KEYWORD2(NULL_CAPS, "NULL")
4551
4679
  TOKEN_KEYWORD2(NULL_TOKEN, "null")
4680
+ TOKEN_KEYWORD(OPTIONAL)
4552
4681
  TOKEN_KEYWORD(OR)
4553
4682
  TOKEN_KEYWORD(ORDER)
4554
4683
  TOKEN_KEYWORD(REMOVE)
@@ -4786,14 +4915,14 @@ struct parser::data
4786
4915
  int id = 0;
4787
4916
  algebra::node_csp parse_call();
4788
4917
  algebra::node_csp parse_create();
4789
- algebra::node_csp parse_match();
4918
+ algebra::node_csp parse_match(bool _optional);
4790
4919
  algebra::node_csp parse_return();
4791
4920
  algebra::node_csp parse_with();
4792
4921
  algebra::node_csp parse_unwind();
4793
4922
  algebra::node_csp parse_delete();
4794
4923
  algebra::node_csp parse_set();
4795
4924
  algebra::node_csp parse_remove();
4796
- algebra::modifiers_csp parse_modifiers();
4925
+ algebra::modifiers_csp parse_modifiers(const std::vector<algebra::named_expression_csp>& _expressions);
4797
4926
  std::vector<algebra::alternative<algebra::graph_node, algebra::graph_edge>> parse_patterns(bool _creation_mode);
4798
4927
  algebra::node_csp parse_map();
4799
4928
  algebra::node_csp parse_properties();
@@ -4973,7 +5102,7 @@ algebra::node_csp parser::data::parse_create()
4973
5102
  return std::make_shared<algebra::create>(parse_patterns(true));
4974
5103
  }
4975
5104
 
4976
- algebra::node_csp parser::data::parse_match()
5105
+ algebra::node_csp parser::data::parse_match(bool _optional)
4977
5106
  {
4978
5107
  get_next_token();
4979
5108
  std::vector<algebra::alternative<algebra::graph_node, algebra::graph_edge>> patterns = parse_patterns(false);
@@ -4982,8 +5111,12 @@ algebra::node_csp parser::data::parse_match()
4982
5111
  {
4983
5112
  get_next_token();
4984
5113
  where = parse_expression();
5114
+ if(expression_analyser.start(where).aggregation_result)
5115
+ {
5116
+ report_error(tok, exception_code::invalid_aggregation, "in where expression.");
5117
+ }
4985
5118
  }
4986
- return std::make_shared<algebra::match>(patterns, where);
5119
+ return std::make_shared<algebra::match>(patterns, where, _optional);
4987
5120
  }
4988
5121
 
4989
5122
  std::vector<algebra::named_expression_csp> parser::data::parse_named_expressions()
@@ -5030,7 +5163,7 @@ algebra::node_csp parser::data::parse_with()
5030
5163
  if(tok.type == token_type::STAR)
5031
5164
  {
5032
5165
  get_next_token();
5033
- return std::make_shared<algebra::with>(true, std::vector<algebra::named_expression_csp>(), parse_modifiers());
5166
+ return std::make_shared<algebra::with>(true, std::vector<algebra::named_expression_csp>(), parse_modifiers({}));
5034
5167
  }
5035
5168
  std::vector<algebra::named_expression_csp> named_expressions = parse_named_expressions();
5036
5169
  std::vector<std::string> new_variables;
@@ -5048,8 +5181,8 @@ algebra::node_csp parser::data::parse_with()
5048
5181
  ++it;
5049
5182
  }
5050
5183
  }
5051
- gqlite_debug("With new variables {} kept variables {}", new_variables, std::views::keys(bounded_variables));
5052
- return std::make_shared<algebra::with>(false, named_expressions, parse_modifiers());
5184
+ // gqlite_debug("With new variables {} kept variables {}", new_variables, std::views::keys(bounded_variables));
5185
+ return std::make_shared<algebra::with>(false, named_expressions, parse_modifiers(named_expressions));
5053
5186
  }
5054
5187
 
5055
5188
  algebra::node_csp parser::data::parse_unwind()
@@ -5218,14 +5351,14 @@ algebra::node_csp parser::data::parse_return()
5218
5351
  if(tok.type == token_type::STAR)
5219
5352
  {
5220
5353
  get_next_token();
5221
- return std::make_shared<algebra::return_statement>(true, std::vector<algebra::named_expression_csp>(), parse_modifiers());
5354
+ return std::make_shared<algebra::return_statement>(true, std::vector<algebra::named_expression_csp>(), parse_modifiers({}));
5222
5355
  }
5223
5356
  std::vector<algebra::named_expression_csp> named_expressions = parse_named_expressions();
5224
5357
  for(algebra::named_expression_csp ne : named_expressions)
5225
5358
  {
5226
5359
  bind_variable(ne->get_name(), ne->get_expression(), expression_analyser.start(ne->get_expression()).type, true);
5227
5360
  }
5228
- return std::make_shared<algebra::return_statement>(false, named_expressions, parse_modifiers());
5361
+ return std::make_shared<algebra::return_statement>(false, named_expressions, parse_modifiers(named_expressions));
5229
5362
  }
5230
5363
 
5231
5364
  namespace
@@ -5241,7 +5374,7 @@ namespace
5241
5374
  };
5242
5375
  }
5243
5376
 
5244
- algebra::modifiers_csp parser::data::parse_modifiers()
5377
+ algebra::modifiers_csp parser::data::parse_modifiers(const std::vector<algebra::named_expression_csp>& _expressions)
5245
5378
  {
5246
5379
  algebra::node_csp skip, limit;
5247
5380
  algebra::order_by_csp order_by;
@@ -5285,6 +5418,24 @@ algebra::modifiers_csp parser::data::parse_modifiers()
5285
5418
  {
5286
5419
  bool asc = true;
5287
5420
  algebra::node_csp expression = parse_expression();
5421
+ if(expression_analyser.start(expression).aggregation_result)
5422
+ {
5423
+ bool has_same = false;
5424
+
5425
+ for(const algebra::named_expression_csp& ne : _expressions)
5426
+ {
5427
+ if(ne->get_expression()->equals(expression))
5428
+ {
5429
+ has_same = true;
5430
+ break;
5431
+ }
5432
+ }
5433
+ if(not has_same)
5434
+ {
5435
+ report_error(tok, exception_code::invalid_aggregation, "in order by expression.");
5436
+ }
5437
+
5438
+ }
5288
5439
  if(tok.type == token_type::ASC)
5289
5440
  {
5290
5441
  get_next_token();
@@ -5660,6 +5811,11 @@ algebra::node_csp parser::data::parse_terminal_expression()
5660
5811
  get_next_token();
5661
5812
  return std::make_shared<algebra::value>(it->second);
5662
5813
  }
5814
+ case token_type::STAR:
5815
+ {
5816
+ get_next_token();
5817
+ return std::make_shared<algebra::all>();
5818
+ }
5663
5819
  default:
5664
5820
  report_unexpected(tok);
5665
5821
  }
@@ -5769,29 +5925,71 @@ algebra::node_csp parser::data::parse_relational_expression()
5769
5925
  }
5770
5926
  }
5771
5927
  case token_type::EQUAL:
5772
- get_next_token();
5773
- return std::make_shared<algebra::relational_equal>(node, parse_relational_expression());
5774
5928
  case token_type::DIFFERENT:
5775
- get_next_token();
5776
- return std::make_shared<algebra::relational_different>(node, parse_relational_expression());
5777
5929
  case token_type::INFERIOR:
5778
- get_next_token();
5779
- return std::make_shared<algebra::relational_inferior>(node, parse_relational_expression());
5780
5930
  case token_type::SUPERIOR:
5781
- get_next_token();
5782
- return std::make_shared<algebra::relational_superior>(node, parse_relational_expression());
5783
5931
  case token_type::INFERIOR_EQUAL:
5784
- get_next_token();
5785
- return std::make_shared<algebra::relational_inferior_equal>(node, parse_relational_expression());
5786
5932
  case token_type::SUPERIOR_EQUAL:
5787
- get_next_token();
5788
- return std::make_shared<algebra::relational_superior_equal>(node, parse_relational_expression());
5933
+ {
5934
+ algebra::node_csp out_node;
5935
+ while(tok.type != token_type::END_OF_FILE)
5936
+ {
5937
+ token_type current_op = tok.type;
5938
+ get_next_token();
5939
+ algebra::node_csp node_rhs = parse_additive_expression();
5940
+ algebra::node_csp current_rel_node;
5941
+ switch (current_op)
5942
+ {
5943
+ case token_type::EQUAL:
5944
+ current_rel_node = std::make_shared<algebra::relational_equal>(node, node_rhs);
5945
+ break;
5946
+ case token_type::DIFFERENT:
5947
+ current_rel_node = std::make_shared<algebra::relational_different>(node, node_rhs);
5948
+ break;
5949
+ case token_type::INFERIOR:
5950
+ current_rel_node = std::make_shared<algebra::relational_inferior>(node, node_rhs);
5951
+ break;
5952
+ case token_type::SUPERIOR:
5953
+ current_rel_node = std::make_shared<algebra::relational_superior>(node, node_rhs);
5954
+ break;
5955
+ case token_type::INFERIOR_EQUAL:
5956
+ current_rel_node = std::make_shared<algebra::relational_inferior_equal>(node, node_rhs);
5957
+ break;
5958
+ case token_type::SUPERIOR_EQUAL:
5959
+ current_rel_node = std::make_shared<algebra::relational_superior_equal>(node, node_rhs);
5960
+ break;
5961
+ default:
5962
+ report_error(tok, exception_code::internal_error, "While parsing relational expression.");
5963
+ }
5964
+ if(out_node)
5965
+ {
5966
+ out_node = std::make_shared<algebra::logical_and>(out_node, current_rel_node);
5967
+ } else {
5968
+ out_node = current_rel_node;
5969
+ }
5970
+ switch(tok.type)
5971
+ {
5972
+ case token_type::EQUAL:
5973
+ case token_type::DIFFERENT:
5974
+ case token_type::INFERIOR:
5975
+ case token_type::SUPERIOR:
5976
+ case token_type::INFERIOR_EQUAL:
5977
+ case token_type::SUPERIOR_EQUAL:
5978
+ node = node_rhs;
5979
+ break;
5980
+ default:
5981
+ return out_node;
5982
+ }
5983
+ }
5984
+ report_unexpected(tok);
5985
+ }
5986
+
5789
5987
  case token_type::IN:
5790
5988
  {
5791
5989
  get_next_token();
5792
5990
  algebra::node_csp container = parse_expression();
5793
5991
  algebra::expression_type container_type = expression_analyser.start(container).type;
5794
- 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.");
5992
+ errors::check_argument_types(container_type, {algebra::expression_type::map, algebra::expression_type::vector, algebra::expression_type::value, algebra::expression_type::empty}, exception_stage::compiletime, "Relational IN can only be applied on vector/map.");
5795
5993
  if(container_type == algebra::expression_type::map)
5796
5994
  {
5797
5995
  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.");
@@ -6021,8 +6219,13 @@ algebra::node_csp parser::parse()
6021
6219
  case token_type::CREATE:
6022
6220
  nodes.push_back(d->parse_create());
6023
6221
  break;
6222
+ case token_type::OPTIONAL:
6223
+ d->get_next_token();
6224
+ d->is_of_type(token_type::MATCH);
6225
+ nodes.push_back(d->parse_match(true));
6226
+ break;
6024
6227
  case token_type::MATCH:
6025
- nodes.push_back(d->parse_match());
6228
+ nodes.push_back(d->parse_match(false));
6026
6229
  break;
6027
6230
  case token_type::WITH:
6028
6231
  nodes.push_back(d->parse_with());
@@ -6175,6 +6378,7 @@ TOKEN_KEYWORD(NOT)
6175
6378
  TOKEN_KEYWORD(MATCH)
6176
6379
  TOKEN_KEYWORD2(NULL_CAPS, "NULL")
6177
6380
  TOKEN_KEYWORD2(NULL_TOKEN, "null")
6381
+ TOKEN_KEYWORD(OPTIONAL)
6178
6382
  TOKEN_KEYWORD(OR)
6179
6383
  TOKEN_KEYWORD(ORDER)
6180
6384
  TOKEN_KEYWORD(REMOVE)
@@ -6245,7 +6449,8 @@ OC_ALGEBRA_GENERATE(create, OC_ALGEBRA_CREATE_NODES_MEMBERS)
6245
6449
 
6246
6450
  #define OC_ALGEBRA_MATCH_NODES_MEMBERS(_KLASS_NAME_, F) \
6247
6451
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns) \
6248
- F(_KLASS_NAME_, node_csp, where)
6452
+ F(_KLASS_NAME_, node_csp, where) \
6453
+ F(_KLASS_NAME_, bool, optional)
6249
6454
 
6250
6455
  OC_ALGEBRA_GENERATE(match, OC_ALGEBRA_MATCH_NODES_MEMBERS)
6251
6456
 
@@ -6347,6 +6552,7 @@ OC_ALGEBRA_GENERATE(edit_labels, OC_ALGEBRA_EDIT_LABELS_MEMBERS)
6347
6552
 
6348
6553
  #define OC_ALGEBRA_NO_MEMBERS(_KLASS_NAME_, F)
6349
6554
 
6555
+ OC_ALGEBRA_GENERATE(all, OC_ALGEBRA_NO_MEMBERS)
6350
6556
  OC_ALGEBRA_GENERATE(end_of_list, OC_ALGEBRA_NO_MEMBERS)
6351
6557
 
6352
6558
  #define OC_ALGEBRA_GRAPH_NODE_MEMBERS(_KLASS_NAME_, F) \
@@ -6371,7 +6577,6 @@ OC_ALGEBRA_GENERATE(graph_edge, OC_ALGEBRA_GRAPH_EDGE_MEMBERS)
6371
6577
 
6372
6578
  OC_ALGEBRA_GENERATE(value, OC_ALGEBRA_VALUE_MEMBERS)
6373
6579
 
6374
-
6375
6580
  #define OC_ALGEBRA_MAP_MEMBERS(_KLASS_NAME_, F) \
6376
6581
  F(_KLASS_NAME_, std::unordered_map<GQLITE_LIST(std::string, node_csp)>, map)
6377
6582
 
@@ -6592,7 +6797,8 @@ OC_ALGEBRA_GENERATE(create, OC_ALGEBRA_CREATE_NODES_MEMBERS)
6592
6797
 
6593
6798
  #define OC_ALGEBRA_MATCH_NODES_MEMBERS(_KLASS_NAME_, F) \
6594
6799
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns) \
6595
- F(_KLASS_NAME_, node_csp, where)
6800
+ F(_KLASS_NAME_, node_csp, where) \
6801
+ F(_KLASS_NAME_, bool, optional)
6596
6802
 
6597
6803
  OC_ALGEBRA_GENERATE(match, OC_ALGEBRA_MATCH_NODES_MEMBERS)
6598
6804
 
@@ -6694,6 +6900,7 @@ OC_ALGEBRA_GENERATE(edit_labels, OC_ALGEBRA_EDIT_LABELS_MEMBERS)
6694
6900
 
6695
6901
  #define OC_ALGEBRA_NO_MEMBERS(_KLASS_NAME_, F)
6696
6902
 
6903
+ OC_ALGEBRA_GENERATE(all, OC_ALGEBRA_NO_MEMBERS)
6697
6904
  OC_ALGEBRA_GENERATE(end_of_list, OC_ALGEBRA_NO_MEMBERS)
6698
6905
 
6699
6906
  #define OC_ALGEBRA_GRAPH_NODE_MEMBERS(_KLASS_NAME_, F) \
@@ -6718,7 +6925,6 @@ OC_ALGEBRA_GENERATE(graph_edge, OC_ALGEBRA_GRAPH_EDGE_MEMBERS)
6718
6925
 
6719
6926
  OC_ALGEBRA_GENERATE(value, OC_ALGEBRA_VALUE_MEMBERS)
6720
6927
 
6721
-
6722
6928
  #define OC_ALGEBRA_MAP_MEMBERS(_KLASS_NAME_, F) \
6723
6929
  F(_KLASS_NAME_, std::unordered_map<GQLITE_LIST(std::string, node_csp)>, map)
6724
6930
 
@@ -7233,11 +7439,17 @@ namespace gqlite::backends::sqlite_oc_executor
7233
7439
  struct sql_query_builder
7234
7440
  {
7235
7441
  std::size_t uid = 0;
7236
- std::size_t table_count = 0;
7237
7442
  std::string variables;
7238
- std::string tables;
7443
+ std::string group_variables;
7444
+ bool has_aggregation_result = false;
7445
+ struct table
7446
+ {
7447
+ sql_join join;
7448
+ std::string table, name;
7449
+ };
7450
+ std::vector<table> tables;
7239
7451
  std::string join_conditions;
7240
- std::string where_conditions;
7452
+ // std::string where_conditions;
7241
7453
  std::string modifiers;
7242
7454
  std::map<int, value> bindings;
7243
7455
  std::size_t next_uid()
@@ -7253,40 +7465,35 @@ namespace gqlite::backends::sqlite_oc_executor
7253
7465
  /**
7254
7466
  * Add a new variable with a generic name format.
7255
7467
  */
7256
- std::string add_variable(const std::string& _expression, const char* _format = "var_{}")
7468
+ std::string add_variable(const std::string& _expression, bool _is_aggregation_result, const char* _format = "var_{}")
7257
7469
  {
7258
7470
  std::string name = format_string(_format, next_uid());
7259
- return add_variable_with_name(_expression, name);
7471
+ return add_variable_with_name(_expression, name, _is_aggregation_result);
7260
7472
  }
7261
7473
  /**
7262
7474
  * Add a new variable with the given name.
7263
7475
  */
7264
- std::string add_variable_with_name(const std::string& _expression, const std::string& _name)
7476
+ std::string add_variable_with_name(const std::string& _expression, const std::string& _name, bool _is_aggregation_result)
7265
7477
  {
7478
+ has_aggregation_result = has_aggregation_result or _is_aggregation_result;
7266
7479
  if(not variables.empty()) variables += ", ";
7267
7480
  variables += format_string("{} AS {}", _expression, _name);
7481
+ if(not _is_aggregation_result) group_variables += _name + ", ";
7268
7482
  return _name;
7269
7483
  }
7484
+ void add_fake_table()
7485
+ {
7486
+ errors::check_condition(tables.empty(), exception_stage::runtime, exception_code::internal_error, "Fake table can only be added when the number of table is 0.");
7487
+ tables.push_back({sql_join::inner, "(SELECT 1 AS fake)", "faketable"});
7488
+ }
7270
7489
  std::string add_table(const std::string& _table, sql_join _join, const char* _table_name_format= "tbl_{}")
7271
7490
  {
7272
- if(table_count == 0)
7491
+ if(tables.empty())
7273
7492
  {
7274
7493
  errors::check_condition(_join == sql_join::inner, exception_stage::runtime, exception_code::internal_error, "Joining with non-inner join on first table.");
7275
- tables += " FROM ";
7276
- } else {
7277
- switch(_join)
7278
- {
7279
- case sql_join::inner:
7280
- tables += " JOIN ";
7281
- break;
7282
- case sql_join::left:
7283
- tables += " LEFT JOIN ";
7284
- break;
7285
- }
7286
7494
  }
7287
- ++table_count;
7288
7495
  std::string table_name = format_string(_table_name_format, next_uid());
7289
- tables += format_string(" {} {} ", _table, table_name);
7496
+ tables.push_back({_join, _table, table_name});
7290
7497
  return table_name;
7291
7498
  }
7292
7499
  template<typename... _T_>
@@ -7299,9 +7506,10 @@ namespace gqlite::backends::sqlite_oc_executor
7299
7506
  template<typename... _T_>
7300
7507
  void add_where_condition(const std::string& _condition, const _T_&... _args)
7301
7508
  {
7302
- if(_condition.empty()) return;
7303
- where_conditions += where_conditions.empty() ? " WHERE " : " AND ";
7304
- where_conditions += format_string(_condition, _args...);
7509
+ add_join_condition(_condition, _args...);
7510
+ // if(_condition.empty()) return;
7511
+ // where_conditions += where_conditions.empty() ? " WHERE " : " AND ";
7512
+ // where_conditions += format_string(_condition, _args...);
7305
7513
  }
7306
7514
  void add_modifier(const std::string& _modifier)
7307
7515
  {
@@ -7311,10 +7519,38 @@ namespace gqlite::backends::sqlite_oc_executor
7311
7519
  {
7312
7520
  std::string q = "SELECT ";
7313
7521
  q += variables.empty() ? " null " : variables;
7314
- q += tables;
7522
+ if(not tables.empty())
7523
+ {
7524
+ // errors::check_condition(not tables.empty(), exception_stage::runtime, exception_code::internal_error, "Missing tables in query.");
7525
+ q += format_string(" FROM {} {}", tables[0].table, tables[0].name);
7526
+ for(std::size_t i = 1; i < tables.size(); ++i)
7527
+ {
7528
+ table& tbl = tables[i];
7529
+ const char* join_type;
7530
+ switch(tbl.join)
7531
+ {
7532
+ case sql_join::inner:
7533
+ join_type = "JOIN";
7534
+ break;
7535
+ case sql_join::left:
7536
+ join_type = "LEFT JOIN";
7537
+ break;
7538
+ }
7539
+ if(i == tables.size() - 1)
7540
+ {
7541
+ q += format_string(" {} {} {}", join_type, tbl.table, tbl.name);
7542
+ } else {
7543
+ q += format_string(" {} ({} {}", join_type, tbl.table, tbl.name);
7544
+ }
7545
+ }
7546
+ for(std::size_t i = 1; i < tables.size() - 1; ++i)
7547
+ {
7548
+ q += ")";
7549
+ }
7550
+ }
7315
7551
  if(not join_conditions.empty())
7316
7552
  {
7317
- if(table_count == 1)
7553
+ if(tables.size() == 1)
7318
7554
  {
7319
7555
  q += " WHERE ";
7320
7556
  } else {
@@ -7322,7 +7558,11 @@ namespace gqlite::backends::sqlite_oc_executor
7322
7558
  }
7323
7559
  q += join_conditions;
7324
7560
  }
7325
- q += where_conditions;
7561
+ // q += where_conditions;
7562
+ if(has_aggregation_result and not group_variables.empty())
7563
+ {
7564
+ q += " GROUP BY {}" % std::string_view(group_variables).substr(0, group_variables.size() - 2);
7565
+ }
7326
7566
  q += modifiers;
7327
7567
  return q;
7328
7568
  }
@@ -7341,7 +7581,7 @@ namespace gqlite::backends::sqlite_oc_executor
7341
7581
  // std::string view_name;
7342
7582
  oc::algebra::visitors::expression_analyser expression_analyser;
7343
7583
 
7344
- table prepare(const table& _table, bool _create_new_table)
7584
+ table prepare(const table& _table, bool _create_new_table, bool _optional)
7345
7585
  {
7346
7586
  expression_analyser.stage = exception_stage::runtime;
7347
7587
  expression_analyser.variables_f = [this](const std::string& _var_name)
@@ -7350,8 +7590,13 @@ namespace gqlite::backends::sqlite_oc_executor
7350
7590
  };
7351
7591
  table new_table;
7352
7592
  if(_create_new_table) new_table.view_name = "__view" + std::to_string(++exec_c->next_view_id);
7353
- if(not _table.view_name.empty())
7593
+ if(_table.view_name.empty())
7354
7594
  {
7595
+ if(_optional)
7596
+ {
7597
+ query_builder.add_fake_table();
7598
+ }
7599
+ } else {
7355
7600
  previous_table_ref = query_builder.add_table(_table.view_name, sql_join::inner, "pt_{}");
7356
7601
 
7357
7602
  for(auto const& [k, v] : _table.variables)
@@ -7360,7 +7605,7 @@ namespace gqlite::backends::sqlite_oc_executor
7360
7605
  variables[k] = { sql_var, v.type };
7361
7606
  if(_create_new_table)
7362
7607
  {
7363
- std::string col_name = query_builder.add_variable(sql_var);
7608
+ std::string col_name = query_builder.add_variable(sql_var, false);
7364
7609
  new_table.variables[k] = { col_name, v.type };
7365
7610
  }
7366
7611
  }
@@ -7428,6 +7673,10 @@ namespace gqlite::backends::sqlite_oc_executor
7428
7673
  {
7429
7674
  return eval_c->query_builder.bind_value(_node->get_value());
7430
7675
  }
7676
+ std::string visit(algebra::all_csp) override
7677
+ {
7678
+ return "*";
7679
+ }
7431
7680
  std::string visit(algebra::has_labels_csp _node) override
7432
7681
  {
7433
7682
  oc::algebra::expression_type et = eval_c->get_variable_info(_node->get_left()).type;
@@ -7463,7 +7712,7 @@ namespace gqlite::backends::sqlite_oc_executor
7463
7712
  {
7464
7713
  if(not first)
7465
7714
  {
7466
- map += " UNION ";
7715
+ map += " UNION ALL ";
7467
7716
  }
7468
7717
  map += "SELECT " + eval_c->query_builder.bind_value(k);
7469
7718
  if(first)
@@ -7488,7 +7737,7 @@ namespace gqlite::backends::sqlite_oc_executor
7488
7737
  {
7489
7738
  if(not first)
7490
7739
  {
7491
- array += " UNION ";
7740
+ array += " UNION ALL ";
7492
7741
  }
7493
7742
  array += "SELECT " + start(v);
7494
7743
  if(first)
@@ -7590,7 +7839,7 @@ namespace gqlite::backends::sqlite_oc_executor
7590
7839
  return "(SELECT json_group_array(key) FROM json_each({}))" % map_expr;
7591
7840
  } else if(_node->get_name() == "toInteger")
7592
7841
  {
7593
- return "CAST({} AS INTEGER)"s % start(_node->get_arguments()[0]);
7842
+ return "gqlite_to_integer({})"s % start(_node->get_arguments()[0]);
7594
7843
  } else if(_node->get_name() == "size")
7595
7844
  {
7596
7845
  return "json_array_length({})"s % start(_node->get_arguments()[0]);
@@ -7603,6 +7852,24 @@ namespace gqlite::backends::sqlite_oc_executor
7603
7852
  } else if(_node->get_name() == "tail")
7604
7853
  {
7605
7854
  return "gqlite_tail({})" % start(_node->get_arguments()[0]);
7855
+ } else if(_node->get_name() == "avg")
7856
+ {
7857
+ return "avg({})" % start(_node->get_arguments()[0]);
7858
+ } else if(_node->get_name() == "count")
7859
+ {
7860
+ return "count({})" % start(_node->get_arguments()[0]);
7861
+ } else if(_node->get_name() == "collect")
7862
+ {
7863
+ return "json_group_array({})" % start(_node->get_arguments()[0]);
7864
+ } else if(_node->get_name() == "max")
7865
+ {
7866
+ return "max({})" % start(_node->get_arguments()[0]);
7867
+ } else if(_node->get_name() == "min")
7868
+ {
7869
+ return "min({})" % start(_node->get_arguments()[0]);
7870
+ } else if(_node->get_name() == "sum")
7871
+ {
7872
+ return "sum({})" % start(_node->get_arguments()[0]);
7606
7873
  }
7607
7874
  throw_exception(exception_stage::runtime, exception_code::unimplemented_error, "Function call to {} is not implemented.", _node->get_name());
7608
7875
  }
@@ -7618,8 +7885,8 @@ namespace gqlite::backends::sqlite_oc_executor
7618
7885
  FILTER_VISITOR_BINARY_OP(relational_different, "!=")
7619
7886
  FILTER_VISITOR_BINARY_OP(relational_inferior, "<")
7620
7887
  FILTER_VISITOR_BINARY_OP(relational_superior, ">")
7621
- FILTER_VISITOR_BINARY_OP(relational_inferior_equal, "<")
7622
- FILTER_VISITOR_BINARY_OP(relational_superior_equal, ">")
7888
+ FILTER_VISITOR_BINARY_OP(relational_inferior_equal, "<=")
7889
+ FILTER_VISITOR_BINARY_OP(relational_superior_equal, ">=")
7623
7890
  FILTER_VISITOR_BINARY_OP(substraction, "-")
7624
7891
  FILTER_VISITOR_BINARY_OP(multiplication, "*")
7625
7892
  FILTER_VISITOR_BINARY_OP(division, "/")
@@ -7726,8 +7993,9 @@ namespace gqlite::backends::sqlite_oc_executor
7726
7993
  {
7727
7994
  sql_evaluation_context mc;
7728
7995
  mc.exec_c = &exec_c;
7729
- mc.prepare(current_table, false);
7996
+ mc.prepare(current_table, false, false);
7730
7997
  sql_expression_visitor sev{&mc};
7998
+ mc.query_builder.add_variable("gqlite_next_uid('nodes')", false);
7731
7999
 
7732
8000
  std::string properties_str;
7733
8001
  if(_node->get_properties())
@@ -7736,9 +8004,9 @@ namespace gqlite::backends::sqlite_oc_executor
7736
8004
  } else {
7737
8005
  properties_str = "'{}'";
7738
8006
  }
7739
- sev.eval_c->query_builder.add_variable(properties_str);
8007
+ mc.query_builder.add_variable(properties_str, false);
7740
8008
  std::string query = sqlite_queries::node_create(
7741
- sev.eval_c->exec_c->graph_name, sev.eval_c->query_builder.assemble());
8009
+ exec_c.graph_name, sev.eval_c->query_builder.assemble());
7742
8010
  value res = exec_c.data->execute_sql(query, sev.eval_c->query_builder.bindings);
7743
8011
  value_vector ret_v;
7744
8012
  value_vector res_v = res.to_vector();
@@ -7792,13 +8060,13 @@ namespace gqlite::backends::sqlite_oc_executor
7792
8060
  {
7793
8061
  sql_evaluation_context mc;
7794
8062
  mc.exec_c = &exec_c;
7795
- mc.prepare(current_table, false);
8063
+ mc.prepare(current_table, false, false);
7796
8064
  sql_expression_visitor sev{&mc};
7797
-
8065
+ mc.query_builder.add_variable("gqlite_next_uid('edges')", false);
7798
8066
  // Handle label
7799
8067
  std::string label = _edge->get_labels().empty() ? std::string() : _edge->get_labels().front();
7800
8068
  std::string label_binding_idx = sev.eval_c->query_builder.bind_value(exec_c.data->id_for_label(label));
7801
- mc.query_builder.add_variable(label_binding_idx, "label_{}");
8069
+ mc.query_builder.add_variable(label_binding_idx, false, "label_{}");
7802
8070
 
7803
8071
  // Handle properties
7804
8072
  std::string properties_str;
@@ -7808,18 +8076,18 @@ namespace gqlite::backends::sqlite_oc_executor
7808
8076
  } else {
7809
8077
  properties_str = "'{}'";
7810
8078
  }
7811
- mc.query_builder.add_variable(properties_str, "properties_{}");
8079
+ mc.query_builder.add_variable(properties_str, false, "properties_{}");
7812
8080
 
7813
8081
  // Handle source
7814
8082
  auto const&[left_value, new_left_value, left_expr] = create_node_if_needed(&sev, _edge->get_source(), _new_nodes, "source_node_{}");
7815
- mc.query_builder.add_variable(left_expr, "source_{}");
8083
+ mc.query_builder.add_variable(left_expr, false, "source_{}");
7816
8084
 
7817
8085
  // Check if destination == source
7818
8086
  auto const&[right_value, new_right_value, right_expr]
7819
8087
  = (_edge->get_source()->get_variable() == _edge->get_destination()->get_variable()) ?
7820
8088
  std::make_tuple(gqlite::value(), false, left_expr)
7821
8089
  : create_node_if_needed(&sev, _edge->get_destination(), _new_nodes, "destination_node_{}");
7822
- mc.query_builder.add_variable(right_expr, "destination_{}");
8090
+ mc.query_builder.add_variable(right_expr, false, "destination_{}");
7823
8091
 
7824
8092
  // Execute queries
7825
8093
  std::string query = sqlite_queries::edge_create(exec_c.graph_name, sev.eval_c->query_builder.assemble());
@@ -7881,7 +8149,7 @@ namespace gqlite::backends::sqlite_oc_executor
7881
8149
  // 2) Create the resulting table
7882
8150
  sql_evaluation_context mc;
7883
8151
  mc.exec_c = &exec_c;
7884
- table new_table = mc.prepare(current_table, true);
8152
+ table new_table = mc.prepare(current_table, true, false);
7885
8153
  sql_expression_visitor sev{&mc};
7886
8154
  std::string first_table_ref;
7887
8155
  // loop over new things
@@ -7901,7 +8169,7 @@ namespace gqlite::backends::sqlite_oc_executor
7901
8169
  } else {
7902
8170
  mc.query_builder.add_join_condition("{}.key = {}.key", first_table_ref, table_ref);
7903
8171
  }
7904
- mc.query_builder.add_variable_with_name(format_string(" {}.value ", table_ref), table_ref);
8172
+ mc.query_builder.add_variable_with_name(format_string(" {}.value ", table_ref), table_ref, false);
7905
8173
  new_table.variables[k] = {table_ref, type};
7906
8174
  }
7907
8175
  }
@@ -7988,7 +8256,7 @@ namespace gqlite::backends::sqlite_oc_executor
7988
8256
  auto it = _mc->variables.find(_oc_var);
7989
8257
  if(it == _mc->variables.end())
7990
8258
  {
7991
- std::string var_name = _mc->query_builder.add_variable(_sql_var, "new_match_{}");
8259
+ std::string var_name = _mc->query_builder.add_variable(_sql_var, false, "new_match_{}");
7992
8260
  _table->variables[_oc_var] = {var_name, _variable_type};
7993
8261
  _mc->variables[_oc_var] = {_sql_var, _variable_type, _sql_properties_var, _label_var};
7994
8262
  } else {
@@ -8002,15 +8270,18 @@ namespace gqlite::backends::sqlite_oc_executor
8002
8270
  {
8003
8271
  sql_match_evaluation_context mc;
8004
8272
  mc.exec_c = &exec_c;
8005
- table new_table = mc.prepare(current_table, true);
8273
+ table new_table = mc.prepare(current_table, true, _node->get_optional());
8006
8274
  sql_match_expression_visitor sev{&mc};
8007
8275
 
8276
+ const bool is_optional = _node->get_optional();
8277
+
8008
8278
  // 1) Go through the patterns
8009
8279
  for(const algebra::alternative<algebra::graph_node, algebra::graph_edge>& pattern : _node->get_patterns())
8010
8280
  {
8011
- pattern.visit<void>([this, &mc, &sev, &new_table](const algebra::graph_node_csp _node)
8281
+ pattern.visit<void>([this, &mc, &sev, &new_table, is_optional](const algebra::graph_node_csp _node)
8012
8282
  {
8013
- std::string table_ref = sev.eval_c->query_builder.add_table(format_string("gqlite_{}_nodes", exec_c.graph_name), sql_join::inner, "node_{}");
8283
+ sql_join s_join = is_optional ? sql_join::left : sql_join::inner;
8284
+ std::string table_ref = sev.eval_c->query_builder.add_table(format_string("gqlite_{}_nodes", exec_c.graph_name), s_join, "node_{}");
8014
8285
  std::string sql_var = format_string("{}.id", table_ref);
8015
8286
  std::string sql_properties_var = format_string("{}.properties", table_ref);
8016
8287
  mc.query_builder.add_join_condition(mc.generate_labels_match(_node->get_labels(), format_string("{}.id", table_ref)));
@@ -8020,14 +8291,15 @@ namespace gqlite::backends::sqlite_oc_executor
8020
8291
  }
8021
8292
  generate_var_match(&mc, &new_table, _node->get_variable(), sql_var, sql_properties_var, std::string(), algebra::expression_type::node);
8022
8293
  },
8023
- [this, &mc, &sev, &new_table](const algebra::graph_edge_csp _edge)
8294
+ [this, &mc, &sev, &new_table, is_optional](const algebra::graph_edge_csp _edge)
8024
8295
  {
8296
+ sql_join s_join = is_optional ? sql_join::left : sql_join::inner;
8025
8297
  std::string table_ref;
8026
8298
  if(_edge->get_directivity() == algebra::edge_directivity::undirected)
8027
8299
  {
8028
- table_ref = sev.eval_c->query_builder.add_table(format_string("gqlite_{}_edges_undirected", exec_c.graph_name), sql_join::inner, "undirected_edge_{}");
8300
+ table_ref = sev.eval_c->query_builder.add_table(format_string("gqlite_{}_edges_undirected", exec_c.graph_name), s_join, "undirected_edge_{}");
8029
8301
  } else {
8030
- table_ref = sev.eval_c->query_builder.add_table(format_string("gqlite_{}_edges", exec_c.graph_name), sql_join::inner, "edge_{}");
8302
+ table_ref = sev.eval_c->query_builder.add_table(format_string("gqlite_{}_edges", exec_c.graph_name), s_join, "edge_{}");
8031
8303
  }
8032
8304
  std::string sql_source_var = format_string("{}.left", table_ref);
8033
8305
  std::string sql_edge_var = format_string("{}.id", table_ref);
@@ -8040,7 +8312,13 @@ namespace gqlite::backends::sqlite_oc_executor
8040
8312
  mc.query_builder.add_where_condition(mc.generate_labels_match(_edge->get_destination()->get_labels(), table_ref + ".right"));
8041
8313
  if(not _edge->get_labels().empty())
8042
8314
  {
8043
- std::string label_conds = "(FALSE ";
8315
+ std::string label_conds;
8316
+ if(is_optional)
8317
+ {
8318
+ label_conds = format_string("({} IS NULL", sql_label_edge_var);
8319
+ } else {
8320
+ label_conds = "(FALSE ";
8321
+ }
8044
8322
  for(const std::string& label : _edge->get_labels())
8045
8323
  {
8046
8324
  label_conds += format_string(" OR {} = {}", sql_label_edge_var, exec_c.data->id_for_label(label));
@@ -8099,10 +8377,10 @@ namespace gqlite::backends::sqlite_oc_executor
8099
8377
  {
8100
8378
  sql_match_evaluation_context mc;
8101
8379
  mc.exec_c = &exec_c;
8102
- mc.prepare(current_table, false);
8380
+ mc.prepare(current_table, false, false);
8103
8381
  sql_match_expression_visitor sev{&mc};
8104
8382
  std::string expr = sev.start(node);
8105
- mc.query_builder.add_variable(expr);
8383
+ mc.query_builder.add_variable(expr, false);
8106
8384
  std::string what = mc.query_builder.assemble();
8107
8385
 
8108
8386
  switch(mc.expression_analyser.start(node).type)
@@ -8111,8 +8389,15 @@ namespace gqlite::backends::sqlite_oc_executor
8111
8389
  {
8112
8390
  if(_ds->get_detach())
8113
8391
  {
8114
- std::string query = sqlite_queries::edge_delete_by_node(exec_c.graph_name, what);
8392
+ std::string query = sqlite_queries::edge_delete_by_nodes(exec_c.graph_name, what);
8115
8393
  mc.exec_c->data->execute_sql(query, mc.query_builder.bindings);
8394
+ } else {
8395
+ std::string query = sqlite_queries::edge_count_by_nodes(exec_c.graph_name, what);
8396
+ gqlite::value count = mc.exec_c->data->execute_sql(query, mc.query_builder.bindings);
8397
+ if(count.to_vector().front().to_vector().front().to_integer() > 0)
8398
+ {
8399
+ throw_exception(exception_stage::runtime, exception_code::delete_connected_node, "delete connected node not allowed, unless using detach.");
8400
+ }
8116
8401
  }
8117
8402
  std::string query = sqlite_queries::node_delete(exec_c.graph_name, what);
8118
8403
  mc.exec_c->data->execute_sql(query, mc.query_builder.bindings);
@@ -8129,59 +8414,6 @@ namespace gqlite::backends::sqlite_oc_executor
8129
8414
  }
8130
8415
  }
8131
8416
  return value();
8132
- #if 0
8133
- evaluation_context eval_c;
8134
- eval_c.table = table;
8135
- evaluator_visitor eval_v;
8136
- eval_v.exec_c = &exec_c;
8137
- eval_v.eval_c = &eval_c;
8138
-
8139
- struct deleter
8140
- {
8141
- statement_visitor* self;
8142
- bool detach;
8143
- void operator()(const node_ref_sp& _node)
8144
- {
8145
- if(detach)
8146
- {
8147
- self->exec_c.data->execute_sql(sqlite_queries::edge_delete_by_node(self->exec_c.graph_name), {{1, _node->id}});
8148
- } else {
8149
- value result = self->exec_c.data->execute_sql(sqlite_queries::edge_count_by_node(self->exec_c.graph_name), {{1, _node->id}});
8150
- value_vector rows = result.to_vector();
8151
- 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());
8152
- value_vector row = rows.front().to_vector();
8153
- 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());
8154
- int count = row.front().to_integer();
8155
- errors::check_condition(count == 0, exception_stage::runtime, exception_code::unspecified, "Cannot delete node with {} relationships.", count);
8156
- }
8157
- self->exec_c.data->execute_sql(sqlite_queries::node_delete(self->exec_c.graph_name), {{1, _node->id}});
8158
- }
8159
- void operator()(const edge_ref_sp& _edge)
8160
- {
8161
- self->exec_c.data->execute_sql(sqlite_queries::edge_delete(self->exec_c.graph_name), {{1, _edge->id}});
8162
- }
8163
- void operator()(const value&)
8164
- {
8165
- throw_exception(exception_stage::runtime, exception_code::invalid_argument_type, "Cannot delete a value.");
8166
- }
8167
- void operator()(const empty&)
8168
- {
8169
- throw_exception(exception_stage::runtime, exception_code::invalid_argument_type, "Cannot delete an empty value.");
8170
- }
8171
- };
8172
-
8173
- for(std::size_t i = 0; i < table.get_rows_count(); ++i)
8174
- {
8175
- value_vector row;
8176
- eval_c.prepare_current_row(table, i, false);
8177
- for(const algebra::node_csp& rv : _ds->get_expressions())
8178
- {
8179
- exec_value ev =eval_v.start(rv);
8180
- std::visit(deleter{this, _ds->get_detach()}, ev);
8181
- }
8182
- }
8183
- return value();
8184
- #endif
8185
8417
  }
8186
8418
  std::string to_sqlite_path(const std::vector<std::string>& _path)
8187
8419
  {
@@ -8209,7 +8441,7 @@ namespace gqlite::backends::sqlite_oc_executor
8209
8441
  {
8210
8442
  sql_match_evaluation_context mc;
8211
8443
  mc.exec_c = &self->exec_c;
8212
- mc.prepare(self->current_table, false);
8444
+ mc.prepare(self->current_table, false, false);
8213
8445
  sql_match_expression_visitor sev{&mc};
8214
8446
  sql_variable_info svi = mc.get_variable_info(_property->get_left());
8215
8447
  // mc.query_builder.add_variable(svi.sql_var_name);
@@ -8238,7 +8470,7 @@ namespace gqlite::backends::sqlite_oc_executor
8238
8470
  {
8239
8471
  sql_match_evaluation_context mc;
8240
8472
  mc.exec_c = &self->exec_c;
8241
- mc.prepare(self->current_table, false);
8473
+ mc.prepare(self->current_table, false, false);
8242
8474
  sql_match_expression_visitor sev{&mc};
8243
8475
  sql_variable_info svi = mc.get_variable_info(_property->get_left());
8244
8476
  std::string expr = sev.start(_property->get_expression());
@@ -8266,14 +8498,15 @@ namespace gqlite::backends::sqlite_oc_executor
8266
8498
  {
8267
8499
  sql_match_evaluation_context mc;
8268
8500
  mc.exec_c = &self->exec_c;
8269
- mc.prepare(self->current_table, false);
8501
+ mc.prepare(self->current_table, false, false);
8270
8502
  sql_match_expression_visitor sev{&mc};
8271
8503
  sql_variable_info svi = mc.get_variable_info(_edit_label->get_target());
8272
8504
  std::vector<std::string> labels = _edit_label->get_labels();
8273
8505
  auto label_ids_view = labels | std::views::transform([this](const std::string& _label) { return value(self->exec_c.data->id_for_label(_label)); });
8274
8506
  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);
8275
- mc.query_builder.add_variable("{}.value" % labels_ref);
8276
- mc.query_builder.add_variable(svi.sql_var_name);
8507
+ mc.query_builder.add_variable("{}.value" % labels_ref, false);
8508
+ mc.query_builder.add_variable(svi.sql_var_name, false);
8509
+ mc.query_builder.add_where_condition("{} IS NOT NULL", svi.sql_var_name);
8277
8510
  std::string what = mc.query_builder.assemble();
8278
8511
 
8279
8512
  switch(svi.type)
@@ -8312,7 +8545,7 @@ namespace gqlite::backends::sqlite_oc_executor
8312
8545
  {
8313
8546
  sql_match_evaluation_context mc;
8314
8547
  mc.exec_c = &self->exec_c;
8315
- mc.prepare(self->current_table, false);
8548
+ mc.prepare(self->current_table, false, false);
8316
8549
  sql_match_expression_visitor sev{&mc};
8317
8550
  sql_variable_info svi = mc.get_variable_info(_property->get_left());
8318
8551
  std::string what = mc.query_builder.assemble().substr(sizeof("SELECT null"));
@@ -8339,13 +8572,13 @@ namespace gqlite::backends::sqlite_oc_executor
8339
8572
  {
8340
8573
  sql_match_evaluation_context mc;
8341
8574
  mc.exec_c = &self->exec_c;
8342
- mc.prepare(self->current_table, false);
8575
+ mc.prepare(self->current_table, false, false);
8343
8576
  sql_match_expression_visitor sev{&mc};
8344
8577
  sql_variable_info svi = mc.get_variable_info(_edit_label->get_target());
8345
8578
  std::vector<std::string> labels = _edit_label->get_labels();
8346
8579
  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)); });
8347
8580
  std::string labels_ref = string::join(label_ids_view, ", ");
8348
- mc.query_builder.add_variable(svi.sql_var_name);
8581
+ mc.query_builder.add_variable(svi.sql_var_name, false);
8349
8582
  std::string what = mc.query_builder.assemble();
8350
8583
 
8351
8584
  switch(svi.type)
@@ -8410,16 +8643,16 @@ namespace gqlite::backends::sqlite_oc_executor
8410
8643
 
8411
8644
  sql_evaluation_context mc;
8412
8645
  mc.exec_c = &exec_c;
8413
- table new_table = mc.prepare(current_table, true);
8646
+ table new_table = mc.prepare(current_table, true, false);
8414
8647
  new_table.variables.clear();
8415
8648
  sql_expression_visitor sev{&mc};
8416
8649
  // Generate SQL Query
8417
8650
  for(const algebra::named_expression_csp& rv : w->get_expressions())
8418
8651
  {
8419
- std::string var = mc.query_builder.add_variable(sev.start(rv->get_expression()));
8420
- algebra::expression_type et = mc.expression_analyser.start(rv->get_expression()).type;
8421
- new_table.variables[rv->get_name()] = {var, et};
8422
- mc.variables[rv->get_name()] = {var, et};
8652
+ algebra::expression_info expr_info = mc.expression_analyser.start(rv->get_expression());
8653
+ std::string var = mc.query_builder.add_variable(sev.start(rv->get_expression()), expr_info.aggregation_result);
8654
+ new_table.variables[rv->get_name()] = {var, expr_info.type};
8655
+ mc.variables[rv->get_name()] = {var, expr_info.type};
8423
8656
  }
8424
8657
  add_filter_expressions(&sev, w->get_modifiers());
8425
8658
  std::string query = "CREATE TEMPORARY TABLE " + new_table.view_name + " AS " + mc.query_builder.assemble();
@@ -8434,10 +8667,10 @@ namespace gqlite::backends::sqlite_oc_executor
8434
8667
  {
8435
8668
  sql_evaluation_context mc;
8436
8669
  mc.exec_c = &exec_c;
8437
- table new_table = mc.prepare(current_table, true);
8670
+ table new_table = mc.prepare(current_table, true, false);
8438
8671
  sql_expression_visitor sev{&mc};
8439
8672
  std::string table_ref = mc.query_builder.add_table("json_each(" + sev.start(uw->get_expression()) + ")", sql_join::inner, "a_uw_{}");
8440
- std::string var = mc.query_builder.add_variable(format_string("{}.value", table_ref));
8673
+ std::string var = mc.query_builder.add_variable(format_string("{}.value", table_ref), false);
8441
8674
  std::string query = "CREATE TEMPORARY TABLE " + new_table.view_name + " AS " + mc.query_builder.assemble();
8442
8675
  new_table.variables[uw->get_name()] = {var, oc::algebra::expression_type::value};
8443
8676
  exec_c.data->execute_sql(query, mc.query_builder.bindings);
@@ -8450,7 +8683,7 @@ namespace gqlite::backends::sqlite_oc_executor
8450
8683
  {
8451
8684
  sql_evaluation_context mc;
8452
8685
  mc.exec_c = &exec_c;
8453
- mc.prepare(current_table, false);
8686
+ mc.prepare(current_table, false, false);
8454
8687
  sql_expression_visitor sev{&mc};
8455
8688
  std::vector<gqlite::oc::algebra::named_expression_csp> expressions;
8456
8689
  if(_rs->get_all())
@@ -8472,14 +8705,14 @@ namespace gqlite::backends::sqlite_oc_executor
8472
8705
 
8473
8706
  for(const algebra::named_expression_csp& rv : expressions)
8474
8707
  {
8475
- oc::algebra::expression_type et = mc.expression_analyser.start(rv->get_expression()).type;
8476
- switch(et)
8708
+ oc::algebra::expression_info expr_info = mc.expression_analyser.start(rv->get_expression());
8709
+ switch(expr_info.type)
8477
8710
  {
8478
8711
  case oc::algebra::expression_type::node:
8479
8712
  {
8480
8713
  std::string table_ref = mc.query_builder.add_table(format_string("gqlite_{}_nodes_as_json", exec_c.graph_name), sql_join::inner, "node_{}");
8481
- mc.query_builder.add_join_condition("{}.id = {}", table_ref, sev.start(rv->get_expression()));
8482
- std::string var_expr = mc.query_builder.add_variable("{}.node" % table_ref);
8714
+ mc.query_builder.add_join_condition("{}.id IS {}", table_ref, sev.start(rv->get_expression()));
8715
+ std::string var_expr = mc.query_builder.add_variable("{}.node" % table_ref, expr_info.aggregation_result);
8483
8716
  variables_extra[rv->get_name()] = {var_expr, oc::algebra::expression_type::node,
8484
8717
  "json_extract({}, '$.properties')" % var_expr, "json_extract({}, '$.labels')" % var_expr};
8485
8718
  break;
@@ -8487,8 +8720,8 @@ namespace gqlite::backends::sqlite_oc_executor
8487
8720
  case oc::algebra::expression_type::edge:
8488
8721
  {
8489
8722
  std::string table_ref = mc.query_builder.add_table(format_string("gqlite_{}_edges_as_json", exec_c.graph_name), sql_join::inner, "edge_{}");
8490
- mc.query_builder.add_join_condition("{}.id = {}", table_ref, sev.start(rv->get_expression()));
8491
- std::string var_expr = mc.query_builder.add_variable("{}.edge" % table_ref);
8723
+ mc.query_builder.add_join_condition("{}.id IS {}", table_ref, sev.start(rv->get_expression()));
8724
+ std::string var_expr = mc.query_builder.add_variable("{}.edge" % table_ref, expr_info.aggregation_result);
8492
8725
  variables_extra[rv->get_name()] = {var_expr, oc::algebra::expression_type::edge,
8493
8726
  "json_extract({}, '$.properties')" % var_expr, "json_extract({}, '$.label')" % var_expr};
8494
8727
  break;
@@ -8499,18 +8732,19 @@ namespace gqlite::backends::sqlite_oc_executor
8499
8732
  [[fallthrough]];
8500
8733
  default:
8501
8734
  {
8502
- std::string var_expr = mc.query_builder.add_variable(sev.start(rv->get_expression()));
8503
- variables_extra[rv->get_name()] = {var_expr, et};
8735
+ std::string var_expr = mc.query_builder.add_variable(sev.start(rv->get_expression()), expr_info.aggregation_result);
8736
+ variables_extra[rv->get_name()] = {var_expr, expr_info.type};
8504
8737
  break;
8505
8738
  }
8506
8739
  }
8507
8740
  labels.push_back(rv->get_name());
8508
- column_types.push_back(et);
8741
+ column_types.push_back(expr_info.type);
8509
8742
  }
8510
8743
  // Execute SQL query
8511
8744
  if(_rs->get_modifiers())
8512
8745
  {
8513
- mc.variables.insert(variables_extra.begin(), variables_extra.end());
8746
+ std::swap(mc.variables, variables_extra);
8747
+ mc.variables.merge(variables_extra);
8514
8748
  add_filter_expressions(&sev, _rs->get_modifiers());
8515
8749
  }
8516
8750
  value res = exec_c.data->execute_sql(mc.query_builder.assemble(), mc.query_builder.bindings);
@@ -8524,35 +8758,38 @@ namespace gqlite::backends::sqlite_oc_executor
8524
8758
  for(std::size_t c = 0; c < r_v.size(); ++c)
8525
8759
  {
8526
8760
  value& v = r_v[c];
8527
- switch(column_types[c])
8761
+ if(v.get_type() != value_type::invalid)
8528
8762
  {
8529
- case oc::algebra::expression_type::boolean:
8530
- if(v.get_type() != value_type::invalid)
8763
+ switch(column_types[c])
8531
8764
  {
8532
- v = (v.to_integer() == 1);
8533
- }
8534
- break;
8535
- case oc::algebra::expression_type::map:
8536
- case oc::algebra::expression_type::vector:
8537
- v = value::from_json(v.to_string());
8538
- break;
8539
- case oc::algebra::expression_type::node:
8540
- case oc::algebra::expression_type::edge:
8541
- v = value::from_json(v.to_string());
8542
- break;
8543
- default:
8544
- if(v.get_type() == value_type::string)
8545
- {
8546
- // Attempt to parse strings as json
8547
- try
8765
+ case oc::algebra::expression_type::boolean:
8766
+ if(v.get_type() != value_type::invalid)
8548
8767
  {
8549
- v = value::from_json(v.to_string());
8768
+ v = (v.to_integer() == 1);
8550
8769
  }
8551
- catch(const exception&)
8770
+ break;
8771
+ case oc::algebra::expression_type::map:
8772
+ case oc::algebra::expression_type::vector:
8773
+ v = value::from_json(v.to_string());
8774
+ break;
8775
+ case oc::algebra::expression_type::node:
8776
+ case oc::algebra::expression_type::edge:
8777
+ v = value::from_json(v.to_string());
8778
+ break;
8779
+ default:
8780
+ if(v.get_type() == value_type::string)
8552
8781
  {
8553
- // ignore, that probably means it is a real string
8782
+ // Attempt to parse strings as json
8783
+ try
8784
+ {
8785
+ v = value::from_json(v.to_string());
8786
+ }
8787
+ catch(const exception&)
8788
+ {
8789
+ // ignore, that probably means it is a real string
8790
+ }
8791
+ break;
8554
8792
  }
8555
- break;
8556
8793
  }
8557
8794
  }
8558
8795
  }
@@ -8597,6 +8834,10 @@ sqlite::sqlite(void* _db) : d(new data)
8597
8834
  {
8598
8835
  d->execute_sql(sqlite_queries::label_create_table());
8599
8836
  }
8837
+ if(not d->table_has("gqlite_uids"))
8838
+ {
8839
+ d->execute_sql(sqlite_queries::uid_create_table());
8840
+ }
8600
8841
  if(not d->graph_has("default"))
8601
8842
  {
8602
8843
  d->graph_create("default");
@@ -8606,7 +8847,7 @@ sqlite::sqlite(void* _db) : d(new data)
8606
8847
  {
8607
8848
  gqlite::value result = d->execute_sql(sqlite_queries::get_debug_stats("default"));
8608
8849
  value_vector rows = result.to_vector();
8609
- errors::check_condition(rows.size() == 7, exception_stage::runtime, exception_code::internal_error, "Invalid number of debug stats got {} expected 7.", rows.size());
8850
+ errors::check_condition(rows.size() == 9, exception_stage::runtime, exception_code::internal_error, "Invalid number of debug stats got {} expected 7.", rows.size());
8610
8851
  value_map stats;
8611
8852
  stats["nodes_count"] = rows[0].to_vector()[0].to_integer();
8612
8853
  stats["edges_count"] = rows[1].to_vector()[0].to_integer();
@@ -8614,6 +8855,8 @@ sqlite::sqlite(void* _db) : d(new data)
8614
8855
  stats["properties_count"] = rows[3].to_vector()[0].to_integer() + rows[4].to_vector()[0].to_integer();
8615
8856
  stats["labels_count"] = rows[5].to_vector()[0].to_integer();
8616
8857
  stats["labels_assignment_nodes_count"] = rows[6].to_vector()[0].to_integer();
8858
+ stats["used_labels_count"] = rows[7].to_vector()[0].to_integer();
8859
+ stats["used_labels_nodes_count"] = rows[8].to_vector()[0].to_integer();
8617
8860
  return stats;
8618
8861
  };
8619
8862
  }
@@ -8667,6 +8910,13 @@ gqlite::value sqlite::execute_oc_query(oc::algebra::node_csp _node)
8667
8910
 
8668
8911
 
8669
8912
 
8913
+
8914
+
8915
+ // Older version of SQLITE don*t have SQLITE_RESULT_SUBTYPE
8916
+ #ifndef SQLITE_RESULT_SUBTYPE
8917
+ #define SQLITE_RESULT_SUBTYPE 0
8918
+ #endif
8919
+
8670
8920
  namespace gqlite::backends
8671
8921
  {
8672
8922
  template<typename... _TArgs_>
@@ -8692,6 +8942,15 @@ namespace gqlite::backends
8692
8942
  { \
8693
8943
  report_exception(_context, ex); return; \
8694
8944
  }
8945
+
8946
+ #define NULL_CHECK_AT(_ARG_INDEX_) \
8947
+ { \
8948
+ if(sqlite3_value_type(_argv[_ARG_INDEX_]) == SQLITE_NULL) \
8949
+ { \
8950
+ sqlite3_result_null(_context); return ; \
8951
+ } \
8952
+ }
8953
+
8695
8954
  #define NULL_CHECK(_ARGS_COUNT_) \
8696
8955
  for(std::size_t i = 0; i < _ARGS_COUNT_; ++i) \
8697
8956
  { \
@@ -8808,7 +9067,7 @@ namespace gqlite::backends
8808
9067
  using enum value_type;
8809
9068
  if(_value.get_type() == invalid)
8810
9069
  {
8811
- return comparison_result::compared_null;
9070
+ return _container.empty() ? comparison_result::false_result : comparison_result::compared_null;
8812
9071
  }
8813
9072
  bool has_compared_to_null = false;
8814
9073
  for(const value& v_c : _container)
@@ -8834,7 +9093,232 @@ namespace gqlite::backends
8834
9093
  }
8835
9094
  return false;
8836
9095
  }
9096
+ /**
9097
+ * @internal
9098
+ * class use to read json files
9099
+ */
9100
+ struct is_valid_json
9101
+ {
9102
+ struct invalid
9103
+ {};
9104
+ std::stringstream stream;
9105
+ int last_char;
9106
+ /// @brief get the next char
9107
+ void fetch_next_non_space_char()
9108
+ {
9109
+ while(std::isspace(fetch_next_char()))
9110
+ {
9111
+ if(last_char == std::stringstream::traits_type::eof())
9112
+ {
9113
+ return;
9114
+ } else if(stream.fail())
9115
+ {
9116
+ throw invalid();
9117
+ }
9118
+ }
9119
+ };
9120
+ int fetch_next_char()
9121
+ {
9122
+ return (last_char = stream.get());
9123
+ }
9124
+ /// @brief start parsing
9125
+ /// @throw gqlite::exception
9126
+ /// @return the parsed value
9127
+ bool start()
9128
+ {
9129
+ try
9130
+ {
9131
+ fetch_next_non_space_char();
9132
+ validate_value();
9133
+ return true;
9134
+ } catch(const invalid&)
9135
+ {
9136
+ return false;
9137
+ }
9138
+ }
9139
+ static bool validate(const char* _string)
9140
+ {
9141
+ is_valid_json ivj{std::stringstream(_string), 0};
9142
+ return ivj.start();
9143
+ }
9144
+ /// @brief read a string
9145
+ /// @return the string without quotation
9146
+ void validate_string()
9147
+ {
9148
+ is_char('"');
9149
+ fetch_next_char();
9150
+ bool keep_nc = false; // this is used to indicate if the last character was a '\' or not
9151
+ while(keep_nc or last_char != '"')
9152
+ {
9153
+ keep_nc = (not keep_nc and last_char == '\\');
9154
+ fetch_next_char();
9155
+ }
9156
+ fetch_next_char(); // eat the '"'
9157
+ }
9158
+ /// @brief throw an exception if last_char different from @param _c
9159
+ void is_char(int _c)
9160
+ {
9161
+ if(last_char != _c) throw invalid();
9162
+ }
9163
+ /// @brief read the next value
9164
+ /// @return and return it
9165
+ void validate_value()
9166
+ {
9167
+ switch (last_char)
9168
+ {
9169
+ case '{':
9170
+ {
9171
+ // Parse object
9172
+ fetch_next_non_space_char();
9173
+ while(last_char != '}')
9174
+ {
9175
+ validate_string();
9176
+ is_char(':');
9177
+ fetch_next_non_space_char();
9178
+ validate_value();
9179
+ if(last_char == ',')
9180
+ {
9181
+ fetch_next_non_space_char();
9182
+ } else {
9183
+ break;
9184
+ }
9185
+ }
9186
+ is_char('}');
9187
+ fetch_next_non_space_char();
9188
+ return;
9189
+ }
9190
+ case '[':
9191
+ {
9192
+ // Parse array
9193
+ fetch_next_non_space_char();
9194
+ while(last_char != ']')
9195
+ {
9196
+ validate_value();
9197
+ if(last_char == ',')
9198
+ {
9199
+ fetch_next_non_space_char();
9200
+ } else {
9201
+ break;
9202
+ }
9203
+ }
9204
+ is_char(']');
9205
+ fetch_next_non_space_char();
9206
+ return;
9207
+ }
9208
+ case '"':
9209
+ validate_string();
9210
+ return;
9211
+ case 't':
9212
+ {
9213
+ // Parse true
9214
+ fetch_next_non_space_char(); is_char('r');
9215
+ fetch_next_non_space_char(); is_char('u');
9216
+ fetch_next_non_space_char(); is_char('e');
9217
+ fetch_next_non_space_char();
9218
+ return;
9219
+ }
9220
+ case 'f':
9221
+ {
9222
+ // Parse false
9223
+ fetch_next_non_space_char(); is_char('a');
9224
+ fetch_next_non_space_char(); is_char('l');
9225
+ fetch_next_non_space_char(); is_char('s');
9226
+ fetch_next_non_space_char(); is_char('e');
9227
+ fetch_next_non_space_char();
9228
+ return;
9229
+ }
9230
+ case 'n':
9231
+ {
9232
+ // Parse null
9233
+ fetch_next_non_space_char(); is_char('u');
9234
+ fetch_next_non_space_char(); is_char('l');
9235
+ fetch_next_non_space_char(); is_char('l');
9236
+ fetch_next_non_space_char();
9237
+ return;
9238
+ }
9239
+ // Parse number
9240
+ case '-':
9241
+ case '0':
9242
+ case '1':
9243
+ case '2':
9244
+ case '3':
9245
+ case '4':
9246
+ case '5':
9247
+ case '6':
9248
+ case '7':
9249
+ case '8':
9250
+ case '9':
9251
+ {
9252
+ fetch_next_non_space_char();
9253
+ bool is_integer = true;
9254
+ while((last_char >= '0' and last_char <= '9') or last_char == '.' or last_char == 'e' or last_char == '+' or last_char == '-')
9255
+ {
9256
+ is_integer = is_integer and (last_char != '.' and last_char != 'e');
9257
+ fetch_next_non_space_char();
9258
+ }
9259
+ return;
9260
+ }
9261
+ default:
9262
+ throw invalid{};
9263
+ }
9264
+ }
9265
+ };
9266
+ void gqlite_next_uid(sqlite3_context* _context,int,sqlite3_value** _argv)
9267
+ {
9268
+ // We get our db connection from the context
9269
+ sqlite3 *db = sqlite3_context_db_handle(_context);
9270
+ const char* label = reinterpret_cast<const char*>(sqlite3_value_text(_argv[0]));
9271
+ sqlite3_stmt *ps;
9272
+ int rc = sqlite3_prepare_v2(db, sqlite_queries::uid_next().c_str(), -1, &ps, 0);
9273
+ if (rc != SQLITE_OK) {
9274
+ // Something bad happened, we'll come back to look at this later
9275
+ return;
9276
+ }
9277
+ sqlite3_bind_text(ps, 1, label, -1, SQLITE_STATIC);
8837
9278
 
9279
+ rc = sqlite3_step(ps);
9280
+ if (rc == SQLITE_ROW)
9281
+ {
9282
+ sqlite3_result_int64(_context, sqlite3_column_int64(ps, 0));
9283
+ } else {
9284
+ report_error(_context, gqlite::exception_code::internal_error, "No uid counter for '{}'.", label);
9285
+ }
9286
+ sqlite3_finalize(ps);
9287
+ }
9288
+ void gqlite_jsonify(sqlite3_context* _context,int,sqlite3_value** _argv)
9289
+ {
9290
+ constexpr int JSON_TYPE = 74;
9291
+ if(sqlite3_value_subtype(_argv[0]) == JSON_TYPE)
9292
+ {
9293
+ sqlite3_result_subtype(_context, JSON_TYPE);
9294
+ sqlite3_result_text(_context, reinterpret_cast<const char*>(sqlite3_value_text(_argv[0])), -1, SQLITE_TRANSIENT);
9295
+ } else {
9296
+ switch(sqlite3_value_type(_argv[0]))
9297
+ {
9298
+ case SQLITE_NULL:
9299
+ sqlite3_result_null(_context);
9300
+ break;
9301
+ case SQLITE_INTEGER:
9302
+ sqlite3_result_int64(_context, sqlite3_value_int64(_argv[0]));
9303
+ break;
9304
+ case SQLITE_FLOAT:
9305
+ sqlite3_result_double(_context, sqlite3_value_double(_argv[0]));
9306
+ break;
9307
+ case SQLITE_TEXT:
9308
+ {
9309
+ const char* str =reinterpret_cast<const char*>(sqlite3_value_text(_argv[0]));
9310
+ sqlite3_result_text(_context, str, -1, SQLITE_TRANSIENT);
9311
+ if(is_valid_json::validate(str))
9312
+ {
9313
+ sqlite3_result_subtype(_context, JSON_TYPE);
9314
+ }
9315
+ }
9316
+ break;
9317
+ default:
9318
+ report_error(_context, exception_code::invalid_argument_type, "Invalid arguments for addition.");
9319
+ }
9320
+ }
9321
+ }
8838
9322
  void gqlite_range(sqlite3_context* _context,int,sqlite3_value** _argv)
8839
9323
  {
8840
9324
  int start = sqlite3_value_int(_argv[0]);
@@ -8893,7 +9377,7 @@ namespace gqlite::backends
8893
9377
  }
8894
9378
  break;
8895
9379
  default:
8896
- report_error(_context, exception_code::invalid_argument_type, "Inavlid arguments for addition.");
9380
+ report_error(_context, exception_code::invalid_argument_type, "Invalid arguments for addition.");
8897
9381
  }
8898
9382
  } else {
8899
9383
  double left_v = 0.0, right_v = 0.0;
@@ -9013,7 +9497,7 @@ namespace gqlite::backends
9013
9497
  }
9014
9498
  void gqlite_contains(sqlite3_context* _context,int,sqlite3_value** _argv)
9015
9499
  {
9016
- NULL_CHECK(2);
9500
+ NULL_CHECK_AT(0);
9017
9501
  std::string left = reinterpret_cast<const char*>(sqlite3_value_text(_argv[0]));
9018
9502
  value val_left;
9019
9503
  SAFE(val_left = value::from_json(left));
@@ -9022,11 +9506,13 @@ namespace gqlite::backends
9022
9506
  {
9023
9507
  case value_type::vector:
9024
9508
  {
9025
- std::string right = reinterpret_cast<const char*>(sqlite3_value_text(_argv[1]));
9026
- value_vector vv = val_left.to_vector();
9027
9509
  value val_right;
9028
- SAFE(val_right = value::from_json(right));
9029
-
9510
+ value_vector vv = val_left.to_vector();
9511
+ if(sqlite3_value_type(_argv[1]) != SQLITE_NULL)
9512
+ {
9513
+ std::string right = reinterpret_cast<const char*>(sqlite3_value_text(_argv[1]));
9514
+ SAFE(val_right = value::from_json(right));
9515
+ }
9030
9516
  switch(contains(vv, val_right))
9031
9517
  {
9032
9518
  case comparison_result::true_result:
@@ -9076,9 +9562,31 @@ namespace gqlite::backends
9076
9562
  report_error(_context, exception_code::invalid_argument_type, "Invalid argument to function tail, list was expected.");
9077
9563
  }
9078
9564
  }
9079
-
9565
+ void gqlite_to_integer(sqlite3_context* _context,int,sqlite3_value** _argv)
9566
+ {
9567
+ int type = sqlite3_value_type(_argv[0]);
9568
+ switch(type)
9569
+ {
9570
+ case SQLITE_INTEGER:
9571
+ sqlite3_result_int64(_context, sqlite3_value_int64(_argv[0]));
9572
+ break;
9573
+ case SQLITE_FLOAT:
9574
+ sqlite3_result_int64(_context, sqlite3_value_double(_argv[0]));
9575
+ break;
9576
+ case SQLITE_TEXT:
9577
+ {
9578
+ std::string s(reinterpret_cast<const char*>(sqlite3_value_text(_argv[0])));
9579
+ sqlite3_result_int64(_context, std::stol(s));
9580
+ }
9581
+ break;
9582
+ default:
9583
+ report_error(_context, exception_code::invalid_argument_type, "Invalid arguments for toInteger.");
9584
+ }
9585
+ }
9080
9586
  void initialise_sqlite_ext(sqlite3* db)
9081
9587
  {
9588
+ sqlite3_create_function(db, "gqlite_jsonify", 1, SQLITE_SUBTYPE | SQLITE_RESULT_SUBTYPE | SQLITE_UTF8, nullptr, &gqlite_jsonify, nullptr, nullptr);
9589
+ sqlite3_create_function(db, "gqlite_next_uid", 1, SQLITE_UTF8, nullptr, &gqlite_next_uid, nullptr, nullptr);
9082
9590
  sqlite3_create_function(db, "gqlite_range", 2, SQLITE_DETERMINISTIC | SQLITE_UTF8, nullptr, &gqlite_range, nullptr, nullptr);
9083
9591
  sqlite3_create_function(db, "gqlite_concat", 2, SQLITE_DETERMINISTIC | SQLITE_UTF8, nullptr, &gqlite_concat, nullptr, nullptr);
9084
9592
  sqlite3_create_function(db, "gqlite_addition", 2, SQLITE_DETERMINISTIC | SQLITE_UTF8, nullptr, &gqlite_addition, nullptr, nullptr);
@@ -9086,5 +9594,6 @@ namespace gqlite::backends
9086
9594
  sqlite3_create_function(db, "gqlite_range_access", 3, SQLITE_DETERMINISTIC | SQLITE_UTF8, nullptr, &gqlite_range_access, nullptr, nullptr);
9087
9595
  sqlite3_create_function(db, "gqlite_contains", 2, SQLITE_DETERMINISTIC | SQLITE_UTF8, nullptr, &gqlite_contains, nullptr, nullptr);
9088
9596
  sqlite3_create_function(db, "gqlite_tail", 1, SQLITE_DETERMINISTIC | SQLITE_UTF8, nullptr, &gqlite_tail, nullptr, nullptr);
9597
+ sqlite3_create_function(db, "gqlite_to_integer", 1, SQLITE_DETERMINISTIC | SQLITE_UTF8, nullptr, &gqlite_to_integer, nullptr, nullptr);
9089
9598
  }
9090
9599
  }//END backends/sqlite_ext.cpp