gqlite 1.2.0 → 1.2.3

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