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 pub versioning: crate::version::Versioning,
24 pub stamp_index: bool,
27 pub as_of_index: bool,
30}
31
32impl Default for StoreOptions {
33 fn default() -> Self {
34 Self {
35 graph_index: true,
36 text_index: false,
37 versioning: crate::version::Versioning::Off,
38 stamp_index: false,
39 as_of_index: false,
40 }
41 }
42}
43
44impl StoreOptions {
45 pub fn level_change(&self) -> crate::version::LevelChange {
47 crate::version::LevelChange {
48 as_of_index: self.as_of_index.then_some(true),
49 stamp_index: self.stamp_index.then_some(true),
50 ..Default::default()
51 }
52 }
53}
54
55pub fn schema_sql(options: &StoreOptions) -> String {
57 schema_sql_with(options, &[])
58}
59
60pub fn schema_sql_with(options: &StoreOptions, extra: &[Statement]) -> String {
62 let mut out =
63 String::from("-- oxilite schema (generated by oxilite_core::schema::schema_sql)\n");
64 for s in create_schema(options).statements.iter().chain(extra) {
65 out.push_str(&s.sql);
66 out.push_str(";\n");
67 }
68 out
69}
70
71pub fn create_schema(options: &StoreOptions) -> Request {
75 let mut r = base_schema(options);
76 if options.versioning > crate::version::Versioning::Off {
77 let change = crate::version::change_statements(
78 &crate::version::VersionState::default(),
79 options.versioning,
80 &options.level_change(),
81 )
82 .expect("versioning from off is always possible");
83 r.statements.extend(change);
84 }
85 r
86}
87
88pub fn base_schema(options: &StoreOptions) -> Request {
90 let mut s = vec![
91 "CREATE TABLE IF NOT EXISTS oxilite_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL) STRICT",
92 "CREATE TABLE IF NOT EXISTS terms (\
94 id INTEGER PRIMARY KEY, \
95 lex TEXT NOT NULL, \
96 dt TEXT, \
97 lang TEXT, \
98 dir INTEGER, \
99 num REAL, \
100 nt INTEGER, \
101 ts REAL) STRICT",
102 "CREATE INDEX IF NOT EXISTS terms_num ON terms(num) WHERE num IS NOT NULL",
103 "CREATE INDEX IF NOT EXISTS terms_ts ON terms(ts) WHERE ts IS NOT NULL",
104 "CREATE TRIGGER IF NOT EXISTS terms_collision BEFORE INSERT ON terms \
106 WHEN EXISTS (SELECT 1 FROM terms t WHERE t.id = NEW.id AND \
107 (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)) \
108 BEGIN SELECT RAISE(ABORT, 'oxilite: term hash collision'); END",
109 "CREATE TABLE IF NOT EXISTS triple_terms (\
110 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",
111 "CREATE TABLE IF NOT EXISTS quads (\
114 s INTEGER NOT NULL, p INTEGER NOT NULL, o INTEGER NOT NULL, g INTEGER NOT NULL DEFAULT 0, \
115 PRIMARY KEY (s, p, o, g)) WITHOUT ROWID, STRICT",
116 "CREATE INDEX IF NOT EXISTS quads_posg ON quads(p, o, s, g)",
117 "CREATE INDEX IF NOT EXISTS quads_ospg ON quads(o, s, p, g)",
118 "CREATE TABLE IF NOT EXISTS graphs (id INTEGER PRIMARY KEY) STRICT",
119 "CREATE TABLE IF NOT EXISTS stats_pred (\
120 p INTEGER PRIMARY KEY, triples INTEGER NOT NULL, distinct_s INTEGER NOT NULL, distinct_o INTEGER NOT NULL) STRICT",
121 "CREATE TABLE IF NOT EXISTS stats_class (o INTEGER PRIMARY KEY, instances INTEGER NOT NULL) STRICT",
122 "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",
124 "CREATE TABLE IF NOT EXISTS tbox_closure (\
127 kind INTEGER NOT NULL, sub INTEGER NOT NULL, sup INTEGER NOT NULL, PRIMARY KEY (kind, sup, sub)) WITHOUT ROWID, STRICT",
128 "CREATE INDEX IF NOT EXISTS tbox_closure_sub ON tbox_closure(kind, sub, sup)",
129 "CREATE TABLE IF NOT EXISTS quads_inf (\
130 s INTEGER NOT NULL, p INTEGER NOT NULL, o INTEGER NOT NULL, g INTEGER NOT NULL DEFAULT 0, \
131 PRIMARY KEY (s, p, o, g)) WITHOUT ROWID, STRICT",
132 "CREATE INDEX IF NOT EXISTS quads_inf_posg ON quads_inf(p, o, s, g)",
133 "CREATE INDEX IF NOT EXISTS quads_inf_ospg ON quads_inf(o, s, p, g)",
134 "CREATE TABLE IF NOT EXISTS quads_inf_src (\
137 src INTEGER NOT NULL, s INTEGER NOT NULL, p INTEGER NOT NULL, o INTEGER NOT NULL, g INTEGER NOT NULL DEFAULT 0, \
138 PRIMARY KEY (s, p, o, g, src)) WITHOUT ROWID, STRICT",
139 "CREATE TABLE IF NOT EXISTS inf_producers (id INTEGER PRIMARY KEY, name TEXT NOT NULL) STRICT",
140 "CREATE TABLE IF NOT EXISTS schema_graphs (\
143 g INTEGER PRIMARY KEY, role INTEGER NOT NULL, iri TEXT, version TEXT, sha256 TEXT, \
144 imports TEXT, active INTEGER NOT NULL DEFAULT 1, loaded_at REAL NOT NULL) STRICT",
145 "CREATE INDEX IF NOT EXISTS schema_graphs_role ON schema_graphs(role, active)",
146 "CREATE TABLE IF NOT EXISTS shapes_index (\
149 target TEXT NOT NULL, path TEXT NOT NULL, datatype TEXT, min_count INTEGER, \
150 max_count INTEGER, pattern TEXT, relationship INTEGER NOT NULL DEFAULT 0, \
151 PRIMARY KEY (target, path)) WITHOUT ROWID, STRICT",
152 "CREATE TABLE IF NOT EXISTS shapes_in (\
153 target TEXT NOT NULL, path TEXT NOT NULL, id INTEGER NOT NULL, \
154 lex TEXT, dt TEXT, lang TEXT, dir INTEGER, \
155 PRIMARY KEY (target, path, id)) WITHOUT ROWID, STRICT",
156 "CREATE TABLE IF NOT EXISTS datalog_work (\
161 run INTEGER NOT NULL, rel INTEGER NOT NULL, \
162 c0 INTEGER NOT NULL DEFAULT 0, c1 INTEGER NOT NULL DEFAULT 0, \
163 c2 INTEGER NOT NULL DEFAULT 0, c3 INTEGER NOT NULL DEFAULT 0, \
164 c4 INTEGER NOT NULL DEFAULT 0, c5 INTEGER NOT NULL DEFAULT 0, \
165 PRIMARY KEY (run, rel, c0, c1, c2, c3, c4, c5)) WITHOUT ROWID, STRICT",
166 "CREATE TABLE IF NOT EXISTS update_buffer (\
168 op INTEGER NOT NULL, s INTEGER NOT NULL, p INTEGER NOT NULL, o INTEGER NOT NULL, g INTEGER NOT NULL) STRICT",
169 "CREATE TABLE IF NOT EXISTS oxilite_guard (\
172 graph_does_not_exist INTEGER CHECK (graph_does_not_exist IS NULL), \
173 graph_already_exists INTEGER CHECK (graph_already_exists IS NULL), \
174 computed_value_not_storable INTEGER CHECK (computed_value_not_storable IS NULL)) STRICT",
175 ]
176 .into_iter()
177 .map(Statement::from)
178 .collect::<Vec<_>>();
179 if options.graph_index {
180 s.push("CREATE INDEX IF NOT EXISTS quads_gspo ON quads(g, s, p, o)".into());
181 }
182 if options.text_index {
183 s.extend(crate::text::schema_statements());
184 }
185 s.push(
186 format!(
187 "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', '{}')",
188 u8::from(options.graph_index),
189 Tag::Integer as u8
190 )
191 .into(),
192 );
193 Request::atomic(s)
194}