1use crate::encoding::{Tag, INT_OFFSET, PAYLOAD_BITS};
6use crate::sql::{Request, Statement};
7
8pub const SCHEMA_VERSION: &str = "1";
10
11#[derive(Debug, Clone, PartialEq, Eq)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14#[cfg_attr(feature = "serde", serde(default, rename_all = "camelCase"))]
15pub struct StoreOptions {
16 pub graph_index: bool,
18 pub text_index: bool,
20}
21
22impl Default for StoreOptions {
23 fn default() -> Self {
24 Self {
25 graph_index: true,
26 text_index: false,
27 }
28 }
29}
30
31pub fn schema_sql(options: &StoreOptions) -> String {
33 schema_sql_with(options, &[])
34}
35
36pub fn schema_sql_with(options: &StoreOptions, extra: &[Statement]) -> String {
38 let mut out =
39 String::from("-- oxilite schema (generated by oxilite_core::schema::schema_sql)\n");
40 for s in create_schema(options).statements.iter().chain(extra) {
41 out.push_str(&s.sql);
42 out.push_str(";\n");
43 }
44 out
45}
46
47pub fn create_schema(options: &StoreOptions) -> Request {
49 let mut s = vec![
50 "CREATE TABLE IF NOT EXISTS oxilite_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL) STRICT",
51 "CREATE TABLE IF NOT EXISTS terms (\
53 id INTEGER PRIMARY KEY, \
54 lex TEXT NOT NULL, \
55 dt TEXT, \
56 lang TEXT, \
57 dir INTEGER, \
58 num REAL, \
59 nt INTEGER, \
60 ts REAL) STRICT",
61 "CREATE INDEX IF NOT EXISTS terms_num ON terms(num) WHERE num IS NOT NULL",
62 "CREATE INDEX IF NOT EXISTS terms_ts ON terms(ts) WHERE ts IS NOT NULL",
63 "CREATE TRIGGER IF NOT EXISTS terms_collision BEFORE INSERT ON terms \
65 WHEN EXISTS (SELECT 1 FROM terms t WHERE t.id = NEW.id AND \
66 (t.lex IS NOT NEW.lex OR t.dt IS NOT NEW.dt OR t.lang IS NOT NEW.lang OR t.dir IS NOT NEW.dir)) \
67 BEGIN SELECT RAISE(ABORT, 'oxilite: term hash collision'); END",
68 "CREATE TABLE IF NOT EXISTS triple_terms (\
69 id INTEGER PRIMARY KEY, s INTEGER NOT NULL, p INTEGER NOT NULL, o INTEGER NOT NULL, vk TEXT NOT NULL, sk TEXT NOT NULL) STRICT",
70 "CREATE TABLE IF NOT EXISTS quads (\
73 s INTEGER NOT NULL, p INTEGER NOT NULL, o INTEGER NOT NULL, g INTEGER NOT NULL DEFAULT 0, \
74 PRIMARY KEY (s, p, o, g)) WITHOUT ROWID, STRICT",
75 "CREATE INDEX IF NOT EXISTS quads_posg ON quads(p, o, s, g)",
76 "CREATE INDEX IF NOT EXISTS quads_ospg ON quads(o, s, p, g)",
77 "CREATE TABLE IF NOT EXISTS graphs (id INTEGER PRIMARY KEY) STRICT",
78 "CREATE TABLE IF NOT EXISTS stats_pred (\
79 p INTEGER PRIMARY KEY, triples INTEGER NOT NULL, distinct_s INTEGER NOT NULL, distinct_o INTEGER NOT NULL) STRICT",
80 "CREATE TABLE IF NOT EXISTS stats_class (o INTEGER PRIMARY KEY, instances INTEGER NOT NULL) STRICT",
81 "CREATE TABLE IF NOT EXISTS stats_po (p INTEGER NOT NULL, o INTEGER NOT NULL, n INTEGER NOT NULL, PRIMARY KEY (p, o)) WITHOUT ROWID, STRICT",
83 "CREATE TABLE IF NOT EXISTS tbox_closure (\
86 kind INTEGER NOT NULL, sub INTEGER NOT NULL, sup INTEGER NOT NULL, PRIMARY KEY (kind, sup, sub)) WITHOUT ROWID, STRICT",
87 "CREATE INDEX IF NOT EXISTS tbox_closure_sub ON tbox_closure(kind, sub, sup)",
88 "CREATE TABLE IF NOT EXISTS quads_inf (\
89 s INTEGER NOT NULL, p INTEGER NOT NULL, o INTEGER NOT NULL, g INTEGER NOT NULL DEFAULT 0, \
90 PRIMARY KEY (s, p, o, g)) WITHOUT ROWID, STRICT",
91 "CREATE INDEX IF NOT EXISTS quads_inf_posg ON quads_inf(p, o, s, g)",
92 "CREATE INDEX IF NOT EXISTS quads_inf_ospg ON quads_inf(o, s, p, g)",
93 "CREATE TABLE IF NOT EXISTS update_buffer (\
95 op INTEGER NOT NULL, s INTEGER NOT NULL, p INTEGER NOT NULL, o INTEGER NOT NULL, g INTEGER NOT NULL) STRICT",
96 "CREATE TABLE IF NOT EXISTS oxilite_guard (\
99 graph_does_not_exist INTEGER CHECK (graph_does_not_exist IS NULL), \
100 graph_already_exists INTEGER CHECK (graph_already_exists IS NULL), \
101 computed_value_not_storable INTEGER CHECK (computed_value_not_storable IS NULL)) STRICT",
102 ]
103 .into_iter()
104 .map(Statement::from)
105 .collect::<Vec<_>>();
106 if options.graph_index {
107 s.push("CREATE INDEX IF NOT EXISTS quads_gspo ON quads(g, s, p, o)".into());
108 }
109 if options.text_index {
110 s.extend(crate::text::schema_statements());
111 }
112 s.push(
113 format!(
114 "INSERT OR IGNORE INTO oxilite_meta(key, value) VALUES ('schema_version', '{SCHEMA_VERSION}'), ('graph_index', '{}'), ('int_offset', '{INT_OFFSET}'), ('payload_bits', '{PAYLOAD_BITS}'), ('integer_tag', '{}')",
115 u8::from(options.graph_index),
116 Tag::Integer as u8
117 )
118 .into(),
119 );
120 Request::atomic(s)
121}