gqlite 1.2.0 → 1.2.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 (85) hide show
  1. checksums.yaml +4 -4
  2. data/ext/gqliterb/.cargo/config.toml +2 -0
  3. data/ext/gqliterb/Cargo.lock +133 -123
  4. data/ext/gqliterb/Cargo.toml +3 -6
  5. data/ext/gqliterb/src/lib.rs +2 -2
  6. data/ext/gqliterb/vendor/gqlitedb/Cargo.lock +2060 -0
  7. data/ext/gqliterb/vendor/gqlitedb/Cargo.toml +132 -0
  8. data/ext/gqliterb/vendor/gqlitedb/askama.toml +3 -0
  9. data/ext/gqliterb/vendor/gqlitedb/benches/common/mod.rs +25 -0
  10. data/ext/gqliterb/vendor/gqlitedb/benches/common/pokec.rs +185 -0
  11. data/ext/gqliterb/vendor/gqlitedb/benches/pokec_divan.rs +137 -0
  12. data/ext/gqliterb/vendor/gqlitedb/benches/pokec_iai.rs +122 -0
  13. data/ext/gqliterb/vendor/gqlitedb/release.toml +7 -0
  14. data/ext/gqliterb/vendor/gqlitedb/src/aggregators/arithmetic.rs +96 -0
  15. data/ext/gqliterb/vendor/gqlitedb/src/aggregators/containers.rs +33 -0
  16. data/ext/gqliterb/vendor/gqlitedb/src/aggregators/count.rs +35 -0
  17. data/ext/gqliterb/vendor/gqlitedb/src/aggregators/stats.rs +168 -0
  18. data/ext/gqliterb/vendor/gqlitedb/src/aggregators.rs +74 -0
  19. data/ext/gqliterb/vendor/gqlitedb/src/capi.rs +236 -0
  20. data/ext/gqliterb/vendor/gqlitedb/src/compiler/expression_analyser.rs +427 -0
  21. data/ext/gqliterb/vendor/gqlitedb/src/compiler/variables_manager.rs +620 -0
  22. data/ext/gqliterb/vendor/gqlitedb/src/compiler.rs +1106 -0
  23. data/ext/gqliterb/vendor/gqlitedb/src/connection.rs +208 -0
  24. data/ext/gqliterb/vendor/gqlitedb/src/consts.rs +10 -0
  25. data/ext/gqliterb/vendor/gqlitedb/src/error.rs +621 -0
  26. data/ext/gqliterb/vendor/gqlitedb/src/functions/containers.rs +115 -0
  27. data/ext/gqliterb/vendor/gqlitedb/src/functions/edge.rs +20 -0
  28. data/ext/gqliterb/vendor/gqlitedb/src/functions/math.rs +44 -0
  29. data/ext/gqliterb/vendor/gqlitedb/src/functions/node.rs +16 -0
  30. data/ext/gqliterb/vendor/gqlitedb/src/functions/path.rs +48 -0
  31. data/ext/gqliterb/vendor/gqlitedb/src/functions/scalar.rs +86 -0
  32. data/ext/gqliterb/vendor/gqlitedb/src/functions/string.rs +28 -0
  33. data/ext/gqliterb/vendor/gqlitedb/src/functions/value.rs +99 -0
  34. data/ext/gqliterb/vendor/gqlitedb/src/functions.rs +412 -0
  35. data/ext/gqliterb/vendor/gqlitedb/src/graph.rs +268 -0
  36. data/ext/gqliterb/vendor/gqlitedb/src/interpreter/evaluators.rs +1788 -0
  37. data/ext/gqliterb/vendor/gqlitedb/src/interpreter/instructions.rs +262 -0
  38. data/ext/gqliterb/vendor/gqlitedb/src/interpreter/mod.rs +4 -0
  39. data/ext/gqliterb/vendor/gqlitedb/src/lib.rs +42 -0
  40. data/ext/gqliterb/vendor/gqlitedb/src/parser/ast.rs +625 -0
  41. data/ext/gqliterb/vendor/gqlitedb/src/parser/gql.pest +191 -0
  42. data/ext/gqliterb/vendor/gqlitedb/src/parser/parser.rs +1153 -0
  43. data/ext/gqliterb/vendor/gqlitedb/src/parser.rs +4 -0
  44. data/ext/gqliterb/vendor/gqlitedb/src/prelude.rs +8 -0
  45. data/ext/gqliterb/vendor/gqlitedb/src/serialize_with.rs +94 -0
  46. data/ext/gqliterb/vendor/gqlitedb/src/store/pgql.rs +121 -0
  47. data/ext/gqliterb/vendor/gqlitedb/src/store/redb.rs +1250 -0
  48. data/ext/gqliterb/vendor/gqlitedb/src/store/sqlite.rs +994 -0
  49. data/ext/gqliterb/vendor/gqlitedb/src/store.rs +432 -0
  50. data/ext/gqliterb/vendor/gqlitedb/src/tests/compiler.rs +92 -0
  51. data/ext/gqliterb/vendor/gqlitedb/src/tests/evaluators.rs +227 -0
  52. data/ext/gqliterb/vendor/gqlitedb/src/tests/parser.rs +81 -0
  53. data/ext/gqliterb/vendor/gqlitedb/src/tests/store/redb.rs +39 -0
  54. data/ext/gqliterb/vendor/gqlitedb/src/tests/store/sqlite.rs +39 -0
  55. data/ext/gqliterb/vendor/gqlitedb/src/tests/store.rs +462 -0
  56. data/ext/gqliterb/vendor/gqlitedb/src/tests/templates/ast.rs +356 -0
  57. data/ext/gqliterb/vendor/gqlitedb/src/tests/templates/programs.rs +455 -0
  58. data/ext/gqliterb/vendor/gqlitedb/src/tests/templates.rs +2 -0
  59. data/ext/gqliterb/vendor/gqlitedb/src/tests.rs +39 -0
  60. data/ext/gqliterb/vendor/gqlitedb/src/utils.rs +28 -0
  61. data/ext/gqliterb/vendor/gqlitedb/src/value/compare.rs +212 -0
  62. data/ext/gqliterb/vendor/gqlitedb/src/value/contains.rs +47 -0
  63. data/ext/gqliterb/vendor/gqlitedb/src/value/value_map.rs +298 -0
  64. data/ext/gqliterb/vendor/gqlitedb/src/value.rs +559 -0
  65. data/ext/gqliterb/vendor/gqlitedb/src/value_table.rs +616 -0
  66. data/ext/gqliterb/vendor/gqlitedb/templates/sql/sqlite/call_stats.sql +22 -0
  67. data/ext/gqliterb/vendor/gqlitedb/templates/sql/sqlite/edge_count_for_node.sql +3 -0
  68. data/ext/gqliterb/vendor/gqlitedb/templates/sql/sqlite/edge_create.sql +6 -0
  69. data/ext/gqliterb/vendor/gqlitedb/templates/sql/sqlite/edge_delete.sql +1 -0
  70. data/ext/gqliterb/vendor/gqlitedb/templates/sql/sqlite/edge_delete_by_nodes.sql +2 -0
  71. data/ext/gqliterb/vendor/gqlitedb/templates/sql/sqlite/edge_select.sql +139 -0
  72. data/ext/gqliterb/vendor/gqlitedb/templates/sql/sqlite/edge_update.sql +4 -0
  73. data/ext/gqliterb/vendor/gqlitedb/templates/sql/sqlite/graph_create.sql +16 -0
  74. data/ext/gqliterb/vendor/gqlitedb/templates/sql/sqlite/graph_delete.sql +3 -0
  75. data/ext/gqliterb/vendor/gqlitedb/templates/sql/sqlite/metadata_create_table.sql +1 -0
  76. data/ext/gqliterb/vendor/gqlitedb/templates/sql/sqlite/metadata_get.sql +1 -0
  77. data/ext/gqliterb/vendor/gqlitedb/templates/sql/sqlite/metadata_set.sql +1 -0
  78. data/ext/gqliterb/vendor/gqlitedb/templates/sql/sqlite/node_create.sql +1 -0
  79. data/ext/gqliterb/vendor/gqlitedb/templates/sql/sqlite/node_delete.sql +1 -0
  80. data/ext/gqliterb/vendor/gqlitedb/templates/sql/sqlite/node_select.sql +42 -0
  81. data/ext/gqliterb/vendor/gqlitedb/templates/sql/sqlite/node_update.sql +4 -0
  82. data/ext/gqliterb/vendor/gqlitedb/templates/sql/sqlite/table_exists.sql +5 -0
  83. data/ext/gqliterb/vendor/gqlitedb/templates/sql/sqlite/upgrade_from_1_01.sql +2 -0
  84. data/ext/gqliterb/vendor/gqlitedb/templates/sql/sqlite/upgrade_graph_from_1_01.sql +65 -0
  85. metadata +82 -2
@@ -0,0 +1,994 @@
1
+ use std::{cell::RefCell, collections::HashSet, path::PathBuf};
2
+
3
+ use askama::Template;
4
+ use ccutils::pool::{self, Pool};
5
+ use rusqlite::{named_params, types::FromSql, OptionalExtension, ToSql};
6
+ use serde::{Deserialize, Serialize};
7
+
8
+ use crate::{
9
+ prelude::*,
10
+ store::{EdgeResult, TransactionBoxable},
11
+ };
12
+
13
+ // _____ _____ ____ _
14
+ // |_ _|__ | ___| __ ___ _ __ ___ / ___| __ _| |
15
+ // | |/ _ \| |_ | '__/ _ \| '_ ` _ \\___ \ / _` | |
16
+ // | | (_) | _|| | | (_) | | | | | |___) | (_| | |
17
+ // |_|\___/|_| |_| \___/|_| |_| |_|____/ \__, |_|
18
+ // |_|
19
+
20
+ impl rusqlite::ToSql for graph::Key
21
+ {
22
+ fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>>
23
+ {
24
+ Ok(rusqlite::types::ToSqlOutput::Owned(
25
+ rusqlite::types::Value::Blob(self.uuid.to_be_bytes().into()),
26
+ ))
27
+ }
28
+ }
29
+
30
+ impl rusqlite::types::FromSql for graph::Key
31
+ {
32
+ fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self>
33
+ {
34
+ Ok(Self {
35
+ uuid: u128::from_be_bytes(<[u8; 16]>::column_result(value)?),
36
+ })
37
+ }
38
+ }
39
+
40
+ // _____ _ _
41
+ // |_ _| __ __ _ _ __ ___ __ _ ___| |_(_) ___ _ __
42
+ // | || '__/ _` | '_ \/ __|/ _` |/ __| __| |/ _ \| '_ \
43
+ // | || | | (_| | | | \__ \ (_| | (__| |_| | (_) | | | |
44
+ // |_||_| \__,_|_| |_|___/\__,_|\___|\__|_|\___/|_| |_|
45
+
46
+ struct TransactionBase
47
+ {
48
+ connection: pool::Handle<rusqlite::Connection>,
49
+ active: RefCell<bool>,
50
+ }
51
+
52
+ impl Drop for TransactionBase
53
+ {
54
+ fn drop(&mut self)
55
+ {
56
+ if *self.active.borrow()
57
+ {
58
+ if let Err(e) = self.connection.execute("ROLLBACK", ())
59
+ {
60
+ println!(
61
+ "Rollback failed with error {:?}, future use of the connection are likely to fail.",
62
+ e
63
+ );
64
+ }
65
+ }
66
+ }
67
+ }
68
+
69
+ pub(crate) struct ReadTransaction
70
+ {
71
+ transaction_base: TransactionBase,
72
+ }
73
+
74
+ impl super::ReadTransaction for ReadTransaction
75
+ {
76
+ fn discard(mut self) -> Result<()>
77
+ {
78
+ self.transaction_base.connection.execute("ROLLBACK", ())?;
79
+ *self.transaction_base.active.get_mut() = false;
80
+ Ok(())
81
+ }
82
+ }
83
+
84
+ pub(crate) struct WriteTransaction
85
+ {
86
+ transaction_base: TransactionBase,
87
+ }
88
+
89
+ impl super::ReadTransaction for WriteTransaction
90
+ {
91
+ fn discard(mut self) -> Result<()>
92
+ {
93
+ self.transaction_base.connection.execute("ROLLBACK", ())?;
94
+ *self.transaction_base.active.get_mut() = false;
95
+ Ok(())
96
+ }
97
+ }
98
+ impl super::WriteTransaction for WriteTransaction
99
+ {
100
+ fn commit(mut self) -> Result<()>
101
+ {
102
+ self.transaction_base.connection.execute("COMMIT", ())?;
103
+ *self.transaction_base.active.get_mut() = false;
104
+ Ok(())
105
+ }
106
+ }
107
+
108
+ trait GetConnection
109
+ {
110
+ fn get_connection(&self) -> &rusqlite::Connection;
111
+ }
112
+
113
+ impl GetConnection for super::TransactionBox<ReadTransaction, WriteTransaction>
114
+ {
115
+ fn get_connection(&self) -> &rusqlite::Connection
116
+ {
117
+ use std::ops::Deref;
118
+ match self
119
+ {
120
+ super::TransactionBox::Read(read) => read.transaction_base.connection.deref(),
121
+ super::TransactionBox::Write(write) => write.transaction_base.connection.deref(),
122
+ }
123
+ }
124
+ }
125
+
126
+ // _____ _ _
127
+ // |_ _|__ _ __ ___ _ __ | | __ _| |_ ___ ___
128
+ // | |/ _ \ '_ ` _ \| '_ \| |/ _` | __/ _ \/ __|
129
+ // | | __/ | | | | | |_) | | (_| | || __/\__ \
130
+ // |_|\___|_| |_| |_| .__/|_|\__,_|\__\___||___/
131
+ // |_|
132
+
133
+ mod templates
134
+ {
135
+ use askama::Template;
136
+
137
+ // Graph related templates
138
+ #[derive(Template)]
139
+ #[template(path = "sql/sqlite/upgrade_graph_from_1_01.sql", escape = "none")]
140
+ pub(super) struct UpgradeGraphFrom1_01<'a>
141
+ {
142
+ pub graph_name: &'a String,
143
+ }
144
+ #[derive(Template)]
145
+ #[template(path = "sql/sqlite/graph_create.sql", escape = "none")]
146
+ pub(super) struct GraphCreate<'a>
147
+ {
148
+ pub graph_name: &'a String,
149
+ }
150
+ #[derive(Template)]
151
+ #[template(path = "sql/sqlite/graph_delete.sql", escape = "none")]
152
+ pub(super) struct GraphDelete<'a>
153
+ {
154
+ pub graph_name: &'a String,
155
+ }
156
+ // Node related templates
157
+ #[derive(Template)]
158
+ #[template(path = "sql/sqlite/node_create.sql", escape = "none")]
159
+ pub(super) struct NodeCreate<'a>
160
+ {
161
+ pub graph_name: &'a String,
162
+ }
163
+ #[derive(Template)]
164
+ #[template(path = "sql/sqlite/node_delete.sql", escape = "none")]
165
+ pub(super) struct NodeDelete<'a>
166
+ {
167
+ pub graph_name: &'a String,
168
+ pub keys: &'a Vec<String>,
169
+ }
170
+ #[derive(Template)]
171
+ #[template(path = "sql/sqlite/node_update.sql", escape = "none")]
172
+ pub(super) struct NodeUpdate<'a>
173
+ {
174
+ pub graph_name: &'a String,
175
+ }
176
+ #[derive(Template)]
177
+ #[template(path = "sql/sqlite/node_select.sql", escape = "none")]
178
+ pub(super) struct NodeSelect<'a>
179
+ {
180
+ pub graph_name: &'a String,
181
+ pub has_keys: bool,
182
+ pub has_labels: bool,
183
+ pub has_properties: bool,
184
+ }
185
+ // Edge queries
186
+ #[derive(Template)]
187
+ #[template(path = "sql/sqlite/edge_count_for_node.sql", escape = "none")]
188
+ pub(super) struct EdgeCountForNode<'a>
189
+ {
190
+ pub graph_name: &'a String,
191
+ pub keys: &'a Vec<String>,
192
+ }
193
+ #[derive(Template)]
194
+ #[template(path = "sql/sqlite/edge_create.sql", escape = "none")]
195
+ pub(super) struct EdgeCreate<'a>
196
+ {
197
+ pub graph_name: &'a String,
198
+ }
199
+ #[derive(Template)]
200
+ #[template(path = "sql/sqlite/edge_delete_by_nodes.sql", escape = "none")]
201
+ pub(super) struct EdgeDeleteByNodes<'a>
202
+ {
203
+ pub graph_name: &'a String,
204
+ pub keys: &'a Vec<String>,
205
+ }
206
+ #[derive(Template)]
207
+ #[template(path = "sql/sqlite/edge_delete.sql", escape = "none")]
208
+ pub(super) struct EdgeDelete<'a>
209
+ {
210
+ pub graph_name: &'a String,
211
+ pub keys: &'a Vec<String>,
212
+ }
213
+ #[derive(Template)]
214
+ #[template(path = "sql/sqlite/edge_update.sql", escape = "none")]
215
+ pub(super) struct EdgeUpdate<'a>
216
+ {
217
+ pub graph_name: &'a String,
218
+ }
219
+ #[derive(Template)]
220
+ #[template(path = "sql/sqlite/edge_select.sql", escape = "none")]
221
+ pub(super) struct EdgeSelect<'a>
222
+ {
223
+ pub graph_name: &'a String,
224
+ pub is_undirected: bool,
225
+ pub table_suffix: &'a str,
226
+ pub has_edge_keys: bool,
227
+ pub has_edge_labels: bool,
228
+ pub has_edge_properties: bool,
229
+ pub has_n_left_keys: bool,
230
+ pub has_n_left_labels: bool,
231
+ pub has_n_left_properties: bool,
232
+ pub has_n_right_keys: bool,
233
+ pub has_n_right_labels: bool,
234
+ pub has_n_right_properties: bool,
235
+ }
236
+ #[derive(Template)]
237
+ #[template(path = "sql/sqlite/call_stats.sql", escape = "none")]
238
+ pub(super) struct CallStats<'a>
239
+ {
240
+ pub graph_name: &'a String,
241
+ }
242
+ }
243
+
244
+ // ____ _
245
+ // / ___|| |_ ___ _ __ ___
246
+ // \___ \| __/ _ \| '__/ _ \
247
+ // ___) | || (_) | | | __/
248
+ // |____/ \__\___/|_| \___|
249
+
250
+ type TransactionBox = store::TransactionBox<ReadTransaction, WriteTransaction>;
251
+
252
+ fn hex(key: &graph::Key) -> String
253
+ {
254
+ format!("{:032X}", key.uuid)
255
+ }
256
+
257
+ pub(crate) struct Store
258
+ {
259
+ connection: Pool<rusqlite::Connection, Error>,
260
+ }
261
+
262
+ ccutils::assert_impl_all!(Store: Sync, Send);
263
+
264
+ impl Store
265
+ {
266
+ /// Crate a new store, with a default graph
267
+ pub(crate) fn new<P: AsRef<std::path::Path>>(path: P) -> Result<Store>
268
+ {
269
+ use store::Store;
270
+ let path: PathBuf = path.as_ref().into();
271
+ let connection = Pool::new(
272
+ move || Ok(rusqlite::Connection::open(&path)?),
273
+ pool::Options::default().minimum_pool_size(1).pool_size(3),
274
+ )?;
275
+ let s = Self { connection };
276
+
277
+ let mut tx = s.begin_write()?;
278
+ if s.check_if_table_exists(&mut tx, "gqlite_metadata")?
279
+ {
280
+ // gqlite version 1.1 incorrectly use ' instead of " in the version number
281
+ let version_raw = s
282
+ .get_metadata_value::<String>(&mut tx, "version")?
283
+ .replace("'", "\"");
284
+ let version: utils::Version = serde_json::from_str(&version_raw)?;
285
+ if version.major != consts::GQLITE_VERSION.major
286
+ || version.minor != consts::GQLITE_VERSION.minor
287
+ {
288
+ s.upgrade_database(&mut tx, version)?;
289
+ }
290
+ }
291
+ else if !s.check_if_table_exists(&mut tx, "gqlite_metadata")?
292
+ && s.check_if_table_exists(&mut tx, "gqlite_default_nodes")?
293
+ {
294
+ // 1.0 didn't have the metadata table
295
+ s.upgrade_database(
296
+ &mut tx,
297
+ utils::Version {
298
+ major: 1,
299
+ minor: 0,
300
+ patch: 0,
301
+ },
302
+ )?;
303
+ }
304
+ else
305
+ {
306
+ tx.get_connection().execute(
307
+ include_str!("../../templates/sql/sqlite/metadata_create_table.sql"),
308
+ (),
309
+ )?;
310
+ s.set_metadata_value_json(&mut tx, "graphs", &Vec::<String>::new())?;
311
+ s.create_graph(&mut tx, &"default".to_string(), true)?;
312
+ }
313
+ s.set_metadata_value_json(&mut tx, "version", &consts::GQLITE_VERSION)?;
314
+ tx.close()?;
315
+ Ok(s)
316
+ }
317
+ fn upgrade_database(&self, transaction: &mut TransactionBox, from: utils::Version) -> Result<()>
318
+ {
319
+ use crate::store::Store;
320
+ match (from.major, from.minor)
321
+ {
322
+ (1, 0) =>
323
+ {
324
+ // Create a metadata table and add the default graph.
325
+ transaction.get_connection().execute(
326
+ include_str!("../../templates/sql/sqlite/metadata_create_table.sql"),
327
+ (),
328
+ )?;
329
+ self.set_metadata_value_json(transaction, "graphs", &vec!["default".to_string()])?;
330
+ }
331
+ _ =>
332
+ {}
333
+ }
334
+ match (from.major, from.minor)
335
+ {
336
+ (1, 0) | (1, 1) =>
337
+ {
338
+ // uuid function is needed for upgrade
339
+ transaction.get_connection().create_scalar_function(
340
+ "uuid",
341
+ 0,
342
+ rusqlite::functions::FunctionFlags::SQLITE_UTF8,
343
+ |_| {
344
+ let uuid = uuid::Uuid::new_v4();
345
+ let bytes = uuid.as_bytes(); // [u8; 16]
346
+ Ok(rusqlite::types::Value::Blob(bytes.to_vec()))
347
+ },
348
+ )?;
349
+
350
+ for graph in self.graphs_list(transaction)?
351
+ {
352
+ transaction.get_connection().execute_batch(
353
+ templates::UpgradeGraphFrom1_01 { graph_name: &graph }
354
+ .render()?
355
+ .as_str(),
356
+ )?;
357
+ }
358
+ transaction.get_connection().execute_batch(include_str!(
359
+ "../../templates/sql/sqlite/upgrade_from_1_01.sql"
360
+ ))?;
361
+ Ok(())
362
+ }
363
+ _ => Err(
364
+ StoreError::IncompatibleVersion {
365
+ expected: consts::GQLITE_VERSION,
366
+ actual: from,
367
+ }
368
+ .into(),
369
+ ),
370
+ }
371
+ }
372
+ /// Check if table exists
373
+ pub(crate) fn check_if_table_exists(
374
+ &self,
375
+ transaction: &mut TransactionBox,
376
+ table_name: impl Into<String>,
377
+ ) -> Result<bool>
378
+ {
379
+ Ok(transaction.get_connection().query_row(
380
+ include_str!("../../templates/sql/sqlite/table_exists.sql"),
381
+ named_params! {":table_name": table_name.into()},
382
+ |row| Ok(row.get::<_, i32>(0)? == 1),
383
+ )?)
384
+ }
385
+ pub(crate) fn get_metadata_value<T: FromSql>(
386
+ &self,
387
+ transaction: &mut TransactionBox,
388
+ key: impl Into<String>,
389
+ ) -> Result<T>
390
+ {
391
+ Ok(transaction.get_connection().query_row(
392
+ include_str!("../../templates/sql/sqlite/metadata_get.sql"),
393
+ named_params! {":name": key.into()},
394
+ |row| row.get(0),
395
+ )?)
396
+ }
397
+ pub(crate) fn get_metadata_value_or_else<T: FromSql>(
398
+ &self,
399
+ transaction: &mut TransactionBox,
400
+ key: impl Into<String>,
401
+ f: impl FnOnce() -> T,
402
+ ) -> Result<T>
403
+ {
404
+ Ok(
405
+ transaction
406
+ .get_connection()
407
+ .query_row(
408
+ include_str!("../../templates/sql/sqlite/metadata_get.sql"),
409
+ named_params! { ":name": key.into()},
410
+ |row| row.get(0),
411
+ )
412
+ .optional()
413
+ .map(|v| v.unwrap_or_else(f))?,
414
+ )
415
+ }
416
+ pub(crate) fn set_metadata_value(
417
+ &self,
418
+ transaction: &mut TransactionBox,
419
+ key: impl Into<String>,
420
+ value: &impl ToSql,
421
+ ) -> Result<()>
422
+ {
423
+ transaction.get_connection().execute(
424
+ include_str!("../../templates/sql/sqlite/metadata_set.sql"),
425
+ named_params! { ":name": key.into(), ":value": value },
426
+ )?;
427
+ Ok(())
428
+ }
429
+
430
+ pub(crate) fn get_metadata_value_json<T: for<'a> Deserialize<'a>>(
431
+ &self,
432
+ transaction: &mut TransactionBox,
433
+ key: impl Into<String>,
434
+ ) -> Result<T>
435
+ {
436
+ Ok(serde_json::from_str(
437
+ &self.get_metadata_value::<String>(transaction, key)?,
438
+ )?)
439
+ }
440
+ pub(crate) fn get_metadata_value_json_or_else<T: for<'a> Deserialize<'a>>(
441
+ &self,
442
+ transaction: &mut TransactionBox,
443
+ key: impl Into<String>,
444
+ f: impl FnOnce() -> T,
445
+ ) -> Result<T>
446
+ {
447
+ Ok(
448
+ transaction
449
+ .get_connection()
450
+ .query_row(
451
+ include_str!("../../templates/sql/sqlite/metadata_get.sql"),
452
+ named_params! {":name": key.into()},
453
+ |row| row.get::<_, String>(0),
454
+ )
455
+ .optional()
456
+ .map(|v| {
457
+ v.map(|x| serde_json::from_str(&x))
458
+ .unwrap_or_else(|| Ok(f()))
459
+ })??,
460
+ )
461
+ }
462
+ pub(crate) fn set_metadata_value_json(
463
+ &self,
464
+ transaction: &mut TransactionBox,
465
+ key: impl Into<String>,
466
+ value: &impl Serialize,
467
+ ) -> Result<()>
468
+ {
469
+ self.set_metadata_value(transaction, key, &serde_json::to_string(value)?)
470
+ }
471
+ }
472
+
473
+ impl store::Store for Store
474
+ {
475
+ type TransactionBox = TransactionBox;
476
+ fn begin_read(&self) -> Result<Self::TransactionBox>
477
+ {
478
+ let connection = self.connection.get()?;
479
+ connection.execute("BEGIN", ())?;
480
+ Ok(Self::TransactionBox::from_read(ReadTransaction {
481
+ transaction_base: TransactionBase {
482
+ connection,
483
+ active: RefCell::new(true),
484
+ },
485
+ }))
486
+ }
487
+ fn begin_write(&self) -> Result<Self::TransactionBox>
488
+ {
489
+ let connection = self.connection.get()?;
490
+ connection.execute("BEGIN", ())?;
491
+ Ok(Self::TransactionBox::from_write(WriteTransaction {
492
+ transaction_base: TransactionBase {
493
+ connection,
494
+ active: RefCell::new(true),
495
+ },
496
+ }))
497
+ }
498
+ fn graphs_list(&self, transaction: &mut Self::TransactionBox) -> Result<Vec<String>>
499
+ {
500
+ self.get_metadata_value_json(transaction, "graphs")
501
+ }
502
+ fn create_graph(
503
+ &self,
504
+ transaction: &mut Self::TransactionBox,
505
+ graph_name: &String,
506
+ ignore_if_exists: bool,
507
+ ) -> Result<()>
508
+ {
509
+ let mut graphs_list = self.graphs_list(transaction)?;
510
+ if graphs_list.contains(graph_name)
511
+ {
512
+ if ignore_if_exists
513
+ {
514
+ return Ok(());
515
+ }
516
+ else
517
+ {
518
+ return Err(
519
+ StoreError::DuplicatedGraph {
520
+ graph_name: graph_name.to_owned(),
521
+ }
522
+ .into(),
523
+ );
524
+ }
525
+ }
526
+ transaction.get_connection().execute_batch(
527
+ templates::GraphCreate {
528
+ graph_name: &graph_name,
529
+ }
530
+ .render()?
531
+ .as_str(),
532
+ )?;
533
+ graphs_list.push(graph_name.to_owned());
534
+ self.set_metadata_value_json(transaction, "graphs", &graphs_list)?;
535
+ Ok(())
536
+ }
537
+ fn delete_graph(&self, transaction: &mut Self::TransactionBox, graph_name: &String)
538
+ -> Result<()>
539
+ {
540
+ let mut graphs_list = self.graphs_list(transaction)?;
541
+ if graphs_list.contains(graph_name)
542
+ {
543
+ transaction.get_connection().execute_batch(
544
+ templates::GraphDelete {
545
+ graph_name: &graph_name,
546
+ }
547
+ .render()?
548
+ .as_str(),
549
+ )?;
550
+ graphs_list.retain(|x| x != graph_name);
551
+ self.set_metadata_value_json(transaction, "graphs", &graphs_list)?;
552
+
553
+ Ok(())
554
+ }
555
+ else
556
+ {
557
+ Err(
558
+ StoreError::UnknownGraph {
559
+ graph_name: graph_name.to_owned(),
560
+ }
561
+ .into(),
562
+ )
563
+ }
564
+ }
565
+ fn create_nodes<'a, T: Iterator<Item = &'a crate::graph::Node>>(
566
+ &self,
567
+ transaction: &mut Self::TransactionBox,
568
+ graph_name: &String,
569
+ nodes_iter: T,
570
+ ) -> Result<()>
571
+ {
572
+ for x in nodes_iter
573
+ {
574
+ transaction.get_connection().execute(
575
+ templates::NodeCreate {
576
+ graph_name: graph_name.into(),
577
+ }
578
+ .render()?
579
+ .as_str(),
580
+ (
581
+ x.key,
582
+ serde_json::to_string(&x.labels)?,
583
+ serde_json::to_string(&x.properties)?,
584
+ ),
585
+ )?;
586
+ }
587
+ Ok(())
588
+ }
589
+ fn delete_nodes(
590
+ &self,
591
+ transaction: &mut Self::TransactionBox,
592
+ graph_name: &String,
593
+ query: store::SelectNodeQuery,
594
+ detach: bool,
595
+ ) -> Result<()>
596
+ {
597
+ let nodes = self.select_nodes(transaction, graph_name, query)?;
598
+ let nodes_keys: Vec<String> = nodes.into_iter().map(|x| hex(&x.key)).collect();
599
+ if detach
600
+ {
601
+ transaction.get_connection().execute(
602
+ templates::EdgeDeleteByNodes {
603
+ graph_name: &graph_name.into(),
604
+ keys: &nodes_keys,
605
+ }
606
+ .render()?
607
+ .as_str(),
608
+ (),
609
+ )?;
610
+ }
611
+ else
612
+ {
613
+ let count = transaction.get_connection().query_row(
614
+ templates::EdgeCountForNode {
615
+ graph_name: &graph_name,
616
+ keys: &nodes_keys,
617
+ }
618
+ .render()?
619
+ .as_str(),
620
+ (),
621
+ |row| row.get::<_, usize>(0),
622
+ )?;
623
+ if count > 0
624
+ {
625
+ return Err(error::RunTimeError::DeleteConnectedNode.into());
626
+ }
627
+ }
628
+ transaction.get_connection().execute(
629
+ templates::NodeDelete {
630
+ graph_name: &graph_name,
631
+ keys: &nodes_keys,
632
+ }
633
+ .render()?
634
+ .as_str(),
635
+ (),
636
+ )?;
637
+ Ok(())
638
+ }
639
+ fn update_node(
640
+ &self,
641
+ transaction: &mut Self::TransactionBox,
642
+ graph_name: &String,
643
+ node: &crate::graph::Node,
644
+ ) -> Result<()>
645
+ {
646
+ transaction.get_connection().execute(
647
+ templates::NodeUpdate {
648
+ graph_name: &graph_name,
649
+ }
650
+ .render()?
651
+ .as_str(),
652
+ named_params! {
653
+ ":key": node.key,
654
+ ":labels": serde_json::to_string(&node.labels)?,
655
+ ":properties": serde_json::to_string(&node.properties)?
656
+ },
657
+ )?;
658
+ Ok(())
659
+ }
660
+ fn select_nodes(
661
+ &self,
662
+ transaction: &mut Self::TransactionBox,
663
+ graph_name: &String,
664
+ query: store::SelectNodeQuery,
665
+ ) -> Result<Vec<crate::graph::Node>>
666
+ {
667
+ let mut prepared_query = transaction.get_connection().prepare(
668
+ templates::NodeSelect {
669
+ graph_name: graph_name.into(),
670
+ has_keys: query.keys.is_some(),
671
+ has_labels: query.labels.is_some(),
672
+ has_properties: query.properties.is_some(),
673
+ }
674
+ .render()?
675
+ .as_str(),
676
+ )?;
677
+ let mut bindings = Vec::<(&'static str, String)>::new();
678
+ if let Some(keys) = query.keys
679
+ {
680
+ let hex_keys = keys.iter().map(|key| hex(key)).collect::<Vec<_>>();
681
+ bindings.push((":keys", serde_json::to_string(&hex_keys)?));
682
+ }
683
+ if let Some(labels) = query.labels
684
+ {
685
+ bindings.push((":labels", serde_json::to_string(&labels)?));
686
+ }
687
+ if let Some(properties) = query.properties
688
+ {
689
+ bindings.push((":properties", serde_json::to_string(&properties)?));
690
+ }
691
+ let mut it = prepared_query.query(
692
+ bindings
693
+ .iter()
694
+ .map(|(k, v)| (*k, v as &dyn rusqlite::ToSql))
695
+ .collect::<Vec<_>>()
696
+ .as_slice(),
697
+ )?;
698
+ let mut nodes: Vec<graph::Node> = Default::default();
699
+ while let Some(row) = it.next()?
700
+ {
701
+ let key = row.get(0)?;
702
+ let labels = serde_json::from_str(&row.get::<_, String>(1)?)?;
703
+ let properties = serde_json::from_str(&row.get::<_, String>(2)?)?;
704
+ nodes.push(graph::Node {
705
+ key,
706
+ labels,
707
+ properties,
708
+ });
709
+ }
710
+ Ok(nodes)
711
+ }
712
+ fn create_edges<'a, T: Iterator<Item = &'a crate::graph::Edge>>(
713
+ &self,
714
+ transaction: &mut Self::TransactionBox,
715
+ graph_name: &String,
716
+ edges_iter: T,
717
+ ) -> Result<()>
718
+ {
719
+ for x in edges_iter
720
+ {
721
+ transaction.get_connection().execute(
722
+ templates::EdgeCreate {
723
+ graph_name: graph_name.into(),
724
+ }
725
+ .render()?
726
+ .as_str(),
727
+ (
728
+ x.key,
729
+ serde_json::to_string(&x.labels)?,
730
+ serde_json::to_string(&x.properties)?,
731
+ x.source.key,
732
+ x.destination.key,
733
+ ),
734
+ )?;
735
+ }
736
+ Ok(())
737
+ }
738
+ fn delete_edges(
739
+ &self,
740
+ transaction: &mut Self::TransactionBox,
741
+ graph_name: &String,
742
+ query: store::SelectEdgeQuery,
743
+ directivity: crate::graph::EdgeDirectivity,
744
+ ) -> Result<()>
745
+ {
746
+ let edges = self.select_edges(transaction, graph_name, query, directivity)?;
747
+ let edges_keys: Vec<String> = edges.into_iter().map(|x| hex(&x.edge.key)).collect();
748
+ transaction.get_connection().execute(
749
+ templates::EdgeDelete {
750
+ graph_name: &graph_name,
751
+ keys: &edges_keys,
752
+ }
753
+ .render()?
754
+ .as_str(),
755
+ (),
756
+ )?;
757
+ Ok(())
758
+ }
759
+ fn update_edge(
760
+ &self,
761
+ transaction: &mut Self::TransactionBox,
762
+ graph_name: &String,
763
+ edge: &crate::graph::Edge,
764
+ ) -> Result<()>
765
+ {
766
+ transaction.get_connection().execute(
767
+ templates::EdgeUpdate {
768
+ graph_name: &graph_name,
769
+ }
770
+ .render()?
771
+ .as_str(),
772
+ named_params! {
773
+ ":key": edge.key,
774
+ ":labels": serde_json::to_string(&edge.labels)?,
775
+ ":properties": serde_json::to_string(&edge.properties)?
776
+ },
777
+ )?;
778
+ Ok(())
779
+ }
780
+ fn select_edges(
781
+ &self,
782
+ transaction: &mut Self::TransactionBox,
783
+ graph_name: &String,
784
+ query: store::SelectEdgeQuery,
785
+ directivity: crate::graph::EdgeDirectivity,
786
+ ) -> Result<Vec<store::EdgeResult>>
787
+ {
788
+ if query.source.is_select_none() || query.destination.is_select_none()
789
+ {
790
+ return Ok(Default::default());
791
+ }
792
+ let (is_undirected, table_suffix) = match directivity
793
+ {
794
+ graph::EdgeDirectivity::Directed => (false, ""),
795
+ graph::EdgeDirectivity::Undirected => (true, "_undirected"),
796
+ };
797
+ let mut prepared_query = transaction.get_connection().prepare(
798
+ templates::EdgeSelect {
799
+ graph_name: graph_name,
800
+ is_undirected,
801
+ table_suffix: table_suffix,
802
+ has_edge_keys: query.keys.is_some(),
803
+ has_edge_labels: query.labels.is_some(),
804
+ has_edge_properties: query.properties.is_some(),
805
+ has_n_left_keys: query.source.keys.is_some(),
806
+ has_n_left_labels: query.source.labels.is_some(),
807
+ has_n_left_properties: query.source.properties.is_some(),
808
+ has_n_right_keys: query.destination.keys.is_some(),
809
+ has_n_right_labels: query.destination.labels.is_some(),
810
+ has_n_right_properties: query.destination.properties.is_some(),
811
+ }
812
+ .render()?
813
+ .as_str(),
814
+ )?;
815
+
816
+ let mut bindings = Vec::<(&'static str, String)>::new();
817
+
818
+ // Edge queries
819
+ if let Some(keys) = query.keys
820
+ {
821
+ let hex_keys = keys.iter().map(|key| hex(&key)).collect::<Vec<_>>();
822
+ bindings.push((":edge_keys", serde_json::to_string(&hex_keys)?));
823
+ }
824
+ if let Some(labels) = query.labels
825
+ {
826
+ bindings.push((":edge_labels", serde_json::to_string(&labels)?));
827
+ }
828
+ if let Some(properties) = query.properties
829
+ {
830
+ bindings.push((":edge_properties", serde_json::to_string(&properties)?));
831
+ }
832
+
833
+ // Left queries
834
+ if let Some(keys) = query.source.keys
835
+ {
836
+ let hex_keys = keys.iter().map(|key| hex(&key)).collect::<Vec<_>>();
837
+ bindings.push((":n_left_keys", serde_json::to_string(&hex_keys)?));
838
+ }
839
+ if let Some(labels) = query.source.labels
840
+ {
841
+ bindings.push((":n_left_labels", serde_json::to_string(&labels)?));
842
+ }
843
+ if let Some(properties) = query.source.properties
844
+ {
845
+ bindings.push((":n_left_properties", serde_json::to_string(&properties)?));
846
+ }
847
+
848
+ // Right queries
849
+ if let Some(keys) = query.destination.keys
850
+ {
851
+ let hex_keys = keys.iter().map(|key| hex(&key)).collect::<Vec<_>>();
852
+ bindings.push((":n_right_keys", serde_json::to_string(&hex_keys)?));
853
+ }
854
+ if let Some(labels) = query.destination.labels
855
+ {
856
+ bindings.push((":n_right_labels", serde_json::to_string(&labels)?));
857
+ }
858
+ if let Some(properties) = query.destination.properties
859
+ {
860
+ bindings.push((":n_right_properties", serde_json::to_string(&properties)?));
861
+ }
862
+
863
+ // Execute query
864
+ let mut it = prepared_query.query(
865
+ bindings
866
+ .iter()
867
+ .map(|(k, v)| (*k, v as &dyn rusqlite::ToSql))
868
+ .collect::<Vec<_>>()
869
+ .as_slice(),
870
+ )?;
871
+
872
+ let mut edges: Vec<EdgeResult> = Default::default();
873
+ let mut edges_keys: HashSet<u128> = Default::default();
874
+ while let Some(row) = it.next()?
875
+ {
876
+ let edge_key: graph::Key = row.get(0)?;
877
+ let n_left_key = row.get(4)?;
878
+ let n_right_key = row.get(7)?;
879
+
880
+ // This ensure that if (a)-[]->(a) the edge is returned only once. But matching [a]-[]-[b] return the edge twice.
881
+ if n_left_key == n_right_key && edges_keys.contains(&edge_key.uuid)
882
+ {
883
+ continue;
884
+ }
885
+ edges_keys.insert(edge_key.uuid);
886
+ let edge_labels = serde_json::from_str(&row.get::<_, String>(1)?)?;
887
+ let edge_properties = serde_json::from_str(&row.get::<_, String>(2)?)?;
888
+
889
+ let n_left_labels = serde_json::from_str(&row.get::<_, String>(5)?)?;
890
+ let n_left_properties = serde_json::from_str(&row.get::<_, String>(6)?)?;
891
+
892
+ let n_right_labels = serde_json::from_str(&row.get::<_, String>(8)?)?;
893
+ let n_right_properties = serde_json::from_str(&row.get::<_, String>(9)?)?;
894
+
895
+ let source = graph::Node {
896
+ key: n_left_key,
897
+ labels: n_left_labels,
898
+ properties: n_left_properties,
899
+ };
900
+ let destination = graph::Node {
901
+ key: n_right_key,
902
+ labels: n_right_labels,
903
+ properties: n_right_properties,
904
+ };
905
+ let reversed = row.get::<_, u32>(3)? == 1;
906
+ let (source, destination) = if reversed
907
+ {
908
+ (destination, source)
909
+ }
910
+ else
911
+ {
912
+ (source, destination)
913
+ };
914
+
915
+ edges.push(EdgeResult {
916
+ edge: graph::Edge {
917
+ key: edge_key,
918
+ labels: edge_labels,
919
+ properties: edge_properties,
920
+ source,
921
+ destination,
922
+ },
923
+ reversed,
924
+ });
925
+ }
926
+ Ok(edges)
927
+ }
928
+ fn compute_statistics(&self, transaction: &mut Self::TransactionBox)
929
+ -> Result<store::Statistics>
930
+ {
931
+ let (nodes_count, edges_count, labels_nodes_count, properties_count) =
932
+ transaction.get_connection().query_row(
933
+ templates::CallStats {
934
+ graph_name: &"default".to_string(),
935
+ }
936
+ .render()?
937
+ .as_str(),
938
+ (),
939
+ |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?)),
940
+ )?;
941
+ Ok(store::Statistics {
942
+ nodes_count,
943
+ edges_count,
944
+ labels_nodes_count,
945
+ properties_count,
946
+ })
947
+ }
948
+ }
949
+
950
+ #[cfg(test)]
951
+ mod tests
952
+ {
953
+ use crate::{
954
+ prelude::*,
955
+ store::{Store, TransactionBoxable},
956
+ };
957
+ #[test]
958
+ fn test_hex()
959
+ {
960
+ assert_eq!(
961
+ super::hex(&graph::Key {
962
+ uuid: 18580062510968287067562660977870108180
963
+ }),
964
+ "0DFA63CEE7484B0DBFC407697F77F614"
965
+ );
966
+ assert_eq!(
967
+ super::hex(&graph::Key { uuid: 0 }),
968
+ "00000000000000000000000000000000"
969
+ );
970
+ }
971
+ #[test]
972
+ fn test_sqlite_metadata()
973
+ {
974
+ let temp_file = crate::tests::create_tmp_file();
975
+ let store = super::Store::new(temp_file.path()).unwrap();
976
+ let mut tx = store.begin_read().unwrap();
977
+ let version: utils::Version = store.get_metadata_value_json(&mut tx, "version").unwrap();
978
+ assert_eq!(version.major, consts::GQLITE_VERSION.major);
979
+ assert_eq!(version.minor, consts::GQLITE_VERSION.minor);
980
+ assert_eq!(version.patch, consts::GQLITE_VERSION.patch);
981
+ tx.close().unwrap();
982
+ drop(store);
983
+
984
+ // Try to reopen
985
+ let store = super::Store::new(temp_file.path()).unwrap();
986
+ let mut tx = store.begin_read().unwrap();
987
+ let version: utils::Version = store.get_metadata_value_json(&mut tx, "version").unwrap();
988
+ assert_eq!(version.major, consts::GQLITE_VERSION.major);
989
+ assert_eq!(version.minor, consts::GQLITE_VERSION.minor);
990
+ assert_eq!(version.patch, consts::GQLITE_VERSION.patch);
991
+ tx.close().unwrap();
992
+ drop(store);
993
+ }
994
+ }