gqlite 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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