gqlite 1.8.0 → 1.8.2

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/ext/Cargo.toml +8 -8
  3. data/ext/db-index/Cargo.toml +2 -2
  4. data/ext/gqlitedb/Cargo.toml +0 -2
  5. data/ext/gqlitedb/src/aggregators.rs +28 -0
  6. data/ext/gqlitedb/src/compiler/expression_analyser.rs +33 -0
  7. data/ext/gqlitedb/src/compiler/variables_manager.rs +111 -11
  8. data/ext/gqlitedb/src/compiler.rs +506 -48
  9. data/ext/gqlitedb/src/connection.rs +14 -2
  10. data/ext/gqlitedb/src/consts.rs +1 -1
  11. data/ext/gqlitedb/src/error.rs +31 -0
  12. data/ext/gqlitedb/src/interpreter/evaluators.rs +153 -331
  13. data/ext/gqlitedb/src/interpreter/instructions.rs +77 -0
  14. data/ext/gqlitedb/src/interpreter/mod.rs +1 -0
  15. data/ext/gqlitedb/src/interpreter/path/bfs.rs +494 -0
  16. data/ext/gqlitedb/src/interpreter/path/dfs.rs +267 -0
  17. data/ext/gqlitedb/src/interpreter/path/group.rs +418 -0
  18. data/ext/gqlitedb/src/interpreter/path.rs +542 -0
  19. data/ext/gqlitedb/src/planner.rs +231 -6
  20. data/ext/gqlitedb/src/query_result.rs +19 -4
  21. data/ext/gqlitedb/src/store/redb.rs +1 -1
  22. data/ext/gqlitedb/src/tests/compiler.rs +2454 -1
  23. data/ext/gqlitedb/src/tests/connection/postgres.rs +22 -0
  24. data/ext/gqlitedb/src/tests/connection/redb.rs +24 -0
  25. data/ext/gqlitedb/src/tests/connection/sqlite.rs +16 -0
  26. data/ext/gqlitedb/src/tests/connection.rs +101 -0
  27. data/ext/gqlitedb/src/tests/evaluators.rs +2206 -1
  28. data/ext/gqlitedb/src/tests/parser.rs +1225 -0
  29. data/ext/gqlitedb/src/tests/planner.rs +433 -2
  30. data/ext/gqlitedb/src/tests/store/vector_index/postgres.rs +5 -5
  31. data/ext/gqlitedb/src/tests/store/vector_index/sqlite.rs +3 -1
  32. data/ext/gqlitedb/src/tests/templates/ast.rs +294 -1
  33. data/ext/gqlitedb/src/tests/templates/programs.rs +318 -0
  34. data/ext/gqliterb/Cargo.toml +0 -1
  35. data/ext/gqlparser/src/oc/ast.rs +72 -3
  36. data/ext/gqlparser/src/oc/error.rs +18 -0
  37. data/ext/gqlparser/src/oc/lexer.rs +157 -2
  38. data/ext/gqlparser/src/oc/parser/tests.rs +735 -29
  39. data/ext/gqlparser/src/oc/parser.rs +1312 -182
  40. data/ext/gqlparser/src/oc/string.rs +172 -0
  41. data/ext/gqlparser/src/oc.rs +1 -0
  42. data/ext/graphcore/src/value/value_map.rs +2 -2
  43. data/ext/graphcore/src/value.rs +13 -1
  44. metadata +6 -2
  45. data/ext/gqlitedb/src/parser/gql.pest +0 -198
@@ -0,0 +1,172 @@
1
+ use logos::Logos;
2
+
3
+ #[derive(Logos, Debug, PartialEq)]
4
+ enum CypherEscapeToken<'a>
5
+ {
6
+ #[regex(r#"[^\\]+"#, |lex| lex.slice())]
7
+ Text(&'a str),
8
+
9
+ #[token(r"\t")]
10
+ Tab,
11
+
12
+ #[token(r"\b")]
13
+ Backspace,
14
+
15
+ #[token(r"\n")]
16
+ Newline,
17
+
18
+ #[token(r"\r")]
19
+ CarriageReturn,
20
+
21
+ #[token(r"\f")]
22
+ FormFeed,
23
+
24
+ #[token(r"\'")]
25
+ SingleQuote,
26
+
27
+ #[token(r#"\""#)]
28
+ DoubleQuote,
29
+
30
+ #[token(r"\\")]
31
+ Backslash,
32
+
33
+ #[regex(r"\\u[0-9a-fA-F]{4}", |lex| lex.slice())]
34
+ Unicode16(&'a str),
35
+
36
+ #[regex(r"\\U[0-9a-fA-F]{8}", |lex| lex.slice())]
37
+ Unicode32(&'a str),
38
+ }
39
+
40
+ pub(crate) fn unescape_cypher(input: &str) -> String
41
+ {
42
+ let mut output = String::with_capacity(input.len());
43
+
44
+ for token in CypherEscapeToken::lexer(input)
45
+ {
46
+ match token.expect("invalid Cypher escape")
47
+ {
48
+ CypherEscapeToken::Text(s) => output.push_str(s),
49
+ CypherEscapeToken::Tab => output.push('\t'),
50
+ CypherEscapeToken::Backspace => output.push('\u{0008}'),
51
+ CypherEscapeToken::Newline => output.push('\n'),
52
+ CypherEscapeToken::CarriageReturn => output.push('\r'),
53
+ CypherEscapeToken::FormFeed => output.push('\u{000C}'),
54
+ CypherEscapeToken::SingleQuote => output.push('\''),
55
+ CypherEscapeToken::DoubleQuote => output.push('"'),
56
+ CypherEscapeToken::Backslash => output.push('\\'),
57
+
58
+ CypherEscapeToken::Unicode16(s) =>
59
+ {
60
+ let value = u16::from_str_radix(&s[2..], 16).expect("invalid UTF-16 escape");
61
+
62
+ let ch = char::from_u32(value as u32).expect("invalid Unicode scalar");
63
+
64
+ output.push(ch);
65
+ }
66
+
67
+ CypherEscapeToken::Unicode32(s) =>
68
+ {
69
+ let value = u32::from_str_radix(&s[2..], 16).expect("invalid UTF-32 escape");
70
+
71
+ output.push(char::from_u32(value).expect("invalid Unicode scalar"));
72
+ }
73
+ }
74
+ }
75
+
76
+ output
77
+ }
78
+
79
+ #[cfg(test)]
80
+ mod tests
81
+ {
82
+ use super::unescape_cypher;
83
+
84
+ #[test]
85
+ fn plain_text()
86
+ {
87
+ assert_eq!(unescape_cypher("hello world"), "hello world");
88
+ }
89
+
90
+ #[test]
91
+ fn tab()
92
+ {
93
+ assert_eq!(unescape_cypher(r"foo\tbar"), "foo\tbar");
94
+ }
95
+
96
+ #[test]
97
+ fn backspace()
98
+ {
99
+ assert_eq!(unescape_cypher(r"foo\bbar"), "foo\u{0008}bar");
100
+ }
101
+
102
+ #[test]
103
+ fn newline()
104
+ {
105
+ assert_eq!(unescape_cypher(r"foo\nbar"), "foo\nbar");
106
+ }
107
+
108
+ #[test]
109
+ fn carriage_return()
110
+ {
111
+ assert_eq!(unescape_cypher(r"foo\rbar"), "foo\rbar");
112
+ }
113
+
114
+ #[test]
115
+ fn form_feed()
116
+ {
117
+ assert_eq!(unescape_cypher(r"foo\fbar"), "foo\u{000C}bar");
118
+ }
119
+
120
+ #[test]
121
+ fn quotes()
122
+ {
123
+ assert_eq!(unescape_cypher(r#"\'hello\""#), "'hello\"");
124
+ }
125
+
126
+ #[test]
127
+ fn backslash()
128
+ {
129
+ assert_eq!(unescape_cypher(r"foo\\bar"), r"foo\bar");
130
+ }
131
+
132
+ #[test]
133
+ fn unicode_16()
134
+ {
135
+ assert_eq!(unescape_cypher(r"\u0041"), "A");
136
+
137
+ assert_eq!(unescape_cypher(r"\u03BB"), "λ");
138
+ }
139
+
140
+ #[test]
141
+ fn unicode_32()
142
+ {
143
+ assert_eq!(unescape_cypher(r"\U0001F600"), "😀");
144
+ }
145
+
146
+ #[test]
147
+ fn mixed_sequences()
148
+ {
149
+ assert_eq!(
150
+ unescape_cypher(r"Hello\nWorld\t\u0021\U0001F600"),
151
+ "Hello\nWorld\t!😀"
152
+ );
153
+ }
154
+
155
+ #[test]
156
+ fn multiple_text_chunks()
157
+ {
158
+ assert_eq!(unescape_cypher(r"abc\n123\txyz"), "abc\n123\txyz");
159
+ }
160
+
161
+ #[test]
162
+ fn empty_string()
163
+ {
164
+ assert_eq!(unescape_cypher(""), "");
165
+ }
166
+
167
+ #[test]
168
+ fn only_escape_sequences()
169
+ {
170
+ assert_eq!(unescape_cypher("\n\t\r"), "\n\t\r");
171
+ }
172
+ }
@@ -6,6 +6,7 @@ pub mod ast;
6
6
  mod error;
7
7
  mod lexer;
8
8
  mod parser;
9
+ mod string;
9
10
 
10
11
  pub use error::{Error, ErrorKind};
11
12
 
@@ -7,7 +7,7 @@ use std::{
7
7
 
8
8
  use crate::{Value, prelude::*};
9
9
 
10
- type ValueMapInner = std::collections::HashMap<String, Value>;
10
+ type ValueMapInner = std::collections::BTreeMap<String, Value>;
11
11
 
12
12
  /// A map of values.
13
13
  #[derive(Debug, PartialEq, Default, Clone, Deserialize, Serialize)]
@@ -249,7 +249,7 @@ impl FromIterator<(String, Value)> for ValueMap
249
249
  /// all but one of the corresponding values will be dropped.
250
250
  fn from_iter<T: IntoIterator<Item = (String, Value)>>(iter: T) -> ValueMap
251
251
  {
252
- let mut map = ValueMapInner::with_hasher(Default::default());
252
+ let mut map = ValueMapInner::new();
253
253
  map.extend(iter);
254
254
  ValueMap(map)
255
255
  }
@@ -122,6 +122,7 @@ impl Hash for Value
122
122
  {
123
123
  fn hash<H: std::hash::Hasher>(&self, state: &mut H)
124
124
  {
125
+ std::mem::discriminant(self).hash(state);
125
126
  match self
126
127
  {
127
128
  Value::Null =>
@@ -506,7 +507,18 @@ impl std::fmt::Display for Value
506
507
  Value::Float(fl) => write!(f, "{}", fl),
507
508
  Value::String(s) => write!(f, "{}", s),
508
509
  Value::TimeStamp(t) => write!(f, "{}", t),
509
- Value::Array(v) => write!(f, "[{}]", v.iter().map(|x| x.to_string()).join(", ")),
510
+ Value::Array(v) =>
511
+ {
512
+ let formatted = v
513
+ .iter()
514
+ .map(|x| match x
515
+ {
516
+ Value::String(s) => format!("\"{}\"", s),
517
+ _ => x.to_string(),
518
+ })
519
+ .join(", ");
520
+ write!(f, "[{}]", formatted)
521
+ }
510
522
  Value::Tensor(v) => write!(f, "[{}]", v.iter().map(|x| x.to_string()).join(", ")),
511
523
  Value::Map(o) => write!(f, "{}", o),
512
524
  Value::Node(n) => write!(f, "{}", n),
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gqlite
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.0
4
+ version: 1.8.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyrille Berger
@@ -129,8 +129,11 @@ files:
129
129
  - ext/gqlitedb/src/interpreter/evaluators.rs
130
130
  - ext/gqlitedb/src/interpreter/instructions.rs
131
131
  - ext/gqlitedb/src/interpreter/mod.rs
132
+ - ext/gqlitedb/src/interpreter/path.rs
133
+ - ext/gqlitedb/src/interpreter/path/bfs.rs
134
+ - ext/gqlitedb/src/interpreter/path/dfs.rs
135
+ - ext/gqlitedb/src/interpreter/path/group.rs
132
136
  - ext/gqlitedb/src/lib.rs
133
- - ext/gqlitedb/src/parser/gql.pest
134
137
  - ext/gqlitedb/src/planner.rs
135
138
  - ext/gqlitedb/src/prelude.rs
136
139
  - ext/gqlitedb/src/query_result.rs
@@ -233,6 +236,7 @@ files:
233
236
  - ext/gqlparser/src/oc/lexer.rs
234
237
  - ext/gqlparser/src/oc/parser.rs
235
238
  - ext/gqlparser/src/oc/parser/tests.rs
239
+ - ext/gqlparser/src/oc/string.rs
236
240
  - ext/gqlparser/src/prelude.rs
237
241
  - ext/graphcore/Cargo.toml
238
242
  - ext/graphcore/README.MD
@@ -1,198 +0,0 @@
1
- WHITESPACE = _{ " " | "\t" | "\n" | "\r\n" }
2
- COMMENT = _{ ("//" ~ (!"\n" ~ ANY)*) | ("/*" ~ (!"*/" ~ ANY)* ~ "*/") }
3
- ident = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "_")* }
4
- parameter_name = @{ (ASCII_ALPHANUMERIC | "_")* }
5
- char_single = {
6
- !("'" | "\\") ~ ANY
7
- | "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t" | "'")
8
- | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4})
9
- }
10
- char_double = {
11
- !("\"" | "\\") ~ ANY
12
- | "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t" | "'")
13
- | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4})
14
- }
15
- char_tick = {
16
- !("`" | "\\") ~ ANY
17
- | "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t" | "'")
18
- | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4})
19
- }
20
- nothing_ident = @{ "###"+ }
21
-
22
- // Query
23
- queries = _{ SOI ~ query ~ (";" ~ query)* ~ ";"? ~ EOI }
24
-
25
- query = { statement* }
26
-
27
- nothing = { nothing_ident? }
28
-
29
- // fragments
30
-
31
- labels = { ":" ~ label_expression }
32
- pattern = { ident? ~ labels? ~ (map | parameter)? }
33
- node_pattern = { "(" ~ pattern ~ ")" }
34
-
35
- directed_edge_pattern = { (")-[" ~ pattern ~ "]->(") | (nothing ~ ")-->(") }
36
- reversed_edge_pattern = { (")<-[" ~ pattern ~ "]-(") | (nothing ~ ")<--(") }
37
- undirected_edge_pattern = { (")-[" ~ pattern ~ "]-(") | (")<-[" ~ pattern ~ "]->(") | (nothing ~ ")--(") }
38
- edge_pattern = { "(" ~ pattern ~ ((directed_edge_pattern | reversed_edge_pattern | undirected_edge_pattern) ~ pattern)+ ~ ")" }
39
-
40
- path_pattern = { ident ~ "=" ~ "(" ~ pattern ~ (directed_edge_pattern | reversed_edge_pattern | undirected_edge_pattern) ~ pattern ~ ")" }
41
-
42
- node_or_edge_pattern = _{ edge_pattern | node_pattern }
43
- node_or_edge_or_path_pattern = _{ edge_pattern | node_pattern | path_pattern }
44
-
45
- // Modifiers
46
-
47
- order_by_kw = @{ "ORDER" ~ WHITESPACE+ ~ "BY" ~ !ASCII_ALPHA }
48
- limit_kw = @{ "LIMIT" ~ !ASCII_ALPHA }
49
- skip_kw = @{ "SKIP" ~ !ASCII_ALPHA }
50
-
51
- limit = { limit_kw ~ expression }
52
- order_by = { order_by_kw ~ (order_by_desc_expression | order_by_asc_expression) ~ ("," ~ (order_by_desc_expression | order_by_asc_expression))* }
53
- order_by_asc_expression = { expression ~ ("ASCENDING" | "ASC")? }
54
- order_by_desc_expression = { expression ~ ("DESCENDING" | "DESC") }
55
- skip = { skip_kw ~ expression }
56
- modifiers = {
57
- (limit ~ ((order_by ~ skip?) | (skip? ~ order_by?)))
58
- | (order_by ~ ((limit ~ skip?) | (skip? ~ limit?)))
59
- | (skip ~ ((order_by ~ limit?) | (limit? ~ order_by?)))
60
- }
61
-
62
- where_modifier = { "WHERE" ~ expression }
63
-
64
- // Statements
65
- statement = {
66
- create_graph_if_not_exists_statement
67
- | create_graph_statement
68
- | drop_graph_if_exists_statement
69
- | drop_graph_statement
70
- | use_graph_statement
71
- | create_statement
72
- | optional_match_statement
73
- | match_statement
74
- | return_statement
75
- | call_statement
76
- | with_statement
77
- | unwind_statement
78
- | delete_statement
79
- | detach_delete_statement
80
- | set_statement
81
- | remove_statement
82
- }
83
-
84
- star = { "*" }
85
-
86
- create_graph_statement = { "CREATE" ~ "GRAPH" ~ ident }
87
- create_graph_if_not_exists_statement = { "CREATE" ~ "GRAPH" ~ "IF" ~ "NOT" ~ "EXISTS" ~ ident }
88
- drop_graph_statement = { "DROP" ~ "GRAPH" ~ ident }
89
- drop_graph_if_exists_statement = { "DROP" ~ "GRAPH" ~ "IF" ~ "EXISTS" ~ ident }
90
- use_graph_statement = { "USE" ~ ident }
91
-
92
- create_statement = { "CREATE" ~ node_or_edge_pattern ~ ("," ~ node_or_edge_pattern)* }
93
- match_statement = { "MATCH" ~ node_or_edge_or_path_pattern ~ ("," ~ node_or_edge_or_path_pattern)* ~ where_modifier? }
94
- optional_match_statement = { "OPTIONAL" ~ match_statement }
95
- return_statement = { "RETURN" ~ (star | named_expression) ~ ("," ~ named_expression)* ~ modifiers? }
96
- call_statement = { "CALL" ~ function_name ~ "()" }
97
- with_statement = { "WITH" ~ (star | named_expression) ~ ("," ~ named_expression)* ~ where_modifier? ~ modifiers? }
98
- unwind_statement = { "UNWIND" ~ named_expression }
99
- delete_statement = { "DELETE" ~ expression ~ ("," ~ expression)* }
100
- detach_delete_statement = { "DETACH" ~ "DELETE" ~ expression ~ ("," ~ expression)* }
101
- set_statement = { "SET" ~ set_expression ~ ("," ~ set_expression)* }
102
- remove_statement = { "REMOVE" ~ remove_expression ~ ("," ~ remove_expression)* }
103
-
104
- // literals
105
- int = @{ ("+" | "-")? ~ ('0'..'9')+ }
106
- octa_int = @{ ("+" | "-")? ~ "0o" ~ ('0'..'9' | 'a' .. 'f' | 'A' .. 'F')+ }
107
- hexa_int = @{ ("+" | "-")? ~ "0x" ~ ('0'..'9' | 'a' .. 'f' | 'A' .. 'F')+ }
108
- null_lit = { "null" }
109
- true_lit = { "true" }
110
- false_lit = { "false" }
111
- num = @{ (int? ~ "." ~ ASCII_DIGIT+ ~ (^"e" ~ int)?) | (int ~ ^"e" ~ int) }
112
- string_literal = ${ ("'" ~ inner_single ~ "'") | ("\"" ~ inner_double ~ "\"") | ("`" ~ inner_tick ~ "`") }
113
- inner_tick = @{ char_tick* }
114
- inner_double = @{ char_double* }
115
- inner_single = @{ char_single* }
116
- function_name = { ident ~ ("." ~ ident)* }
117
-
118
- // Expression
119
-
120
- set_expression = _{ set_eq_expression | set_add_expression | set_label_expression }
121
- set_eq_expression = { set_eq_member_access ~ "=" ~ expression }
122
- set_add_expression = { set_eq_member_access ~ "+=" ~ expression }
123
- remove_expression = _{ remove_member_access | set_label_expression }
124
- set_label_expression = { ident ~ (":" ~ ident)+ }
125
- set_eq_member_access = { (("(" ~ ident ~ ")") | ident) ~ ("." ~ ident)* }
126
- remove_member_access = { (("(" ~ ident ~ ")") | ident) ~ ("." ~ ident)+ }
127
-
128
- expression = { prefix* ~ expression_term ~ postfix* ~ (infix ~ prefix? ~ expression_term ~ postfix*)* }
129
-
130
- infix = _{ addition | subtraction | multiplication | division | modulo | exponent | or | and | xor | equal | different | in_ | not_in | superior_equal | inferior_equal | superior | inferior }
131
- postfix = _{ is_null | is_not_null | member_access | range_access | range_access_to | index_access }
132
- prefix = _{ negation | not }
133
-
134
- addition = { "+" }
135
- subtraction = { "-" }
136
- multiplication = { "*" }
137
- division = { "/" }
138
- modulo = { "%" }
139
- exponent = { "^" }
140
-
141
- xor_kw = @{ "XOR" ~ !ASCII_ALPHA }
142
- xor = { xor_kw }
143
- or_kw = @{ "OR" ~ !ASCII_ALPHA }
144
- or = { or_kw }
145
- and_kw = @{ "AND" ~ !ASCII_ALPHA }
146
- and = { and_kw }
147
- equal = { "=" }
148
- different = { "<>" }
149
- inferior_kw = @{ "<" ~ (!">" | !"=") }
150
- inferior = { inferior_kw }
151
- superior_kw = @{ ">" ~ !"=" }
152
- superior = { superior_kw }
153
- inferior_equal = { "<=" }
154
- superior_equal = { ">=" }
155
- not_in = { "NOT" ~ "IN" }
156
- in_ = { "IN" }
157
-
158
- negation = { "-" ~ !int }
159
- not_kw = @{ "NOT" ~ !ASCII_ALPHA }
160
- not = { not_kw }
161
-
162
- is_null = { "IS" ~ "NULL" }
163
- is_not_null = { "IS" ~ "NOT" ~ "NULL" }
164
- member_access = { ("." ~ (ident | string_literal))+ }
165
- index_access = { "[" ~ expression ~ "]" }
166
- range_access = { "[" ~ expression ~ ".." ~ expression? ~ "]" }
167
- range_access_to = { "[" ~ ".." ~ expression ~ "]" }
168
-
169
- label_check_expression = { ident ~ (":" ~ ident)+ }
170
-
171
- parenthesised_expression = { "(" ~ expression ~ ")" }
172
- named_expression = { (expression ~ "AS" ~ ident) | expression }
173
- function_call = { function_name ~ "(" ~ (expression ~ ("," ~ expression)*)? ~ ")" }
174
- function_star = { function_name ~ "(" ~ "*" ~ ")" }
175
- expression_term = { null_lit | true_lit | false_lit | num | hexa_int | octa_int | int | string_literal | map | array | label_check_expression | parameter | parenthesised_expression | function_call | function_star | ident }
176
-
177
- parameter = { "$" ~ parameter_name }
178
-
179
- pair = { (ident | string_literal) ~ ":" ~ expression }
180
-
181
- array = {
182
- "[" ~ "]"
183
- | "[" ~ expression ~ ("," ~ expression)* ~ "]"
184
- }
185
-
186
- map = {
187
- "{" ~ "}"
188
- | "{" ~ pair ~ ("," ~ pair)* ~ "}"
189
- }
190
-
191
- // label_expression
192
-
193
- label_expression = _{ label_inclusion }
194
-
195
- label_inclusion = { label_alternative ~ (":" ~ label_expression)* }
196
- label_alternative = { label_atom ~ ("|" ~ label_expression)* }
197
- label_atom = { label_group | ":"? ~ ident }
198
- label_group = { "(" ~ label_expression ~ ")" }