1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
use crate::*;

/// Create a schema in the database by writing to the system Schema table.
pub fn create_schema(db: &DB, name: &str) {
    if let Some(_id) = get_schema(db, name) {
        panic!("schema '{}' already exists", name);
    }
    let t = &db.sys_schema;
    let mut row = t.row();
    row.id = t.alloc_id(db);
    row.values[0] = Value::String(Rc::new(name.to_string()));
    t.insert(db, &mut row);
}

/// Create a new table in the database by writing to the system Table and Column tables.
pub fn create_table(db: &DB, info: &ColInfo) {
    if let Some(_t) = get_table(db, &info.name) {
        panic!("table {} already exists", info.name.str());
    }
    let tid = {
        let schema = &info.name.schema;
        if let Some(schema_id) = get_schema(db, schema) {
            let root = db.alloc_page();
            let t = &db.sys_table;
            let mut row = t.row();
            // Columns are root, schema, name, id_gen
            row.id = t.alloc_id(db);
            row.values[0] = Value::Int(root as i64);
            row.values[1] = Value::Int(schema_id);
            row.values[2] = Value::String(Rc::new(info.name.name.clone()));
            row.values[3] = Value::Int(1);
            t.insert(db, &mut row);
            row.id
        } else {
            panic!("schema not found [{}]", &schema);
        }
    };
    {
        let cnames = &info.colnames;
        let t = &db.sys_column;
        let mut row = t.row();
        row.values[0] = Value::Int(tid);
        for (num, typ) in info.typ.iter().enumerate() {
            // Columns are Table, Name, Type
            row.id = t.alloc_id(db);
            row.values[1] = Value::String(Rc::new(cnames[num].to_string()));
            row.values[2] = Value::Int(*typ as i64);
            t.insert(db, &mut row);
        }
    }
}

/// Create a new table index by writing to the system Index and IndexColumn tables.
pub fn create_index(db: &DB, info: &IndexInfo) {
    if let Some(table) = db.get_table(&info.tname) {
        let root = db.alloc_page();
        let index_id = {
            let t = &db.sys_index;
            let mut row = t.row();
            // Columns are Root, Table, Name
            row.id = t.alloc_id(db);
            row.values[0] = Value::Int(root as i64);
            row.values[1] = Value::Int(table.id);
            row.values[2] = Value::String(Rc::new(info.iname.clone()));
            t.insert(db, &mut row);
            row.id
        };
        {
            let t = &db.sys_index_col;
            let mut row = t.row();
            for cnum in &info.cols {
                // Columns are Index, ColIndex
                row.id = t.alloc_id(db);
                row.values[0] = Value::Int(index_id);
                row.values[1] = Value::Int(*cnum as i64);
                t.insert(db, &mut row);
            }
        }
        if root > SYS_ROOT_LAST {
            table.add_index(root, info.cols.clone(), index_id);
            table.init_index(db);
        }
    } else {
        panic!("table not found: {}", &info.tname.str());
    }
}

/// Create or alter a function in the database by saving the source into the Function system table.
pub fn create_function(db: &DB, name: &ObjRef, source: Rc<String>, alter: bool) {
    if let Some(schema_id) = get_schema(db, &name.schema) {
        let t = &db.sys_function;
        if alter {
            // Columns are Schema(0), Name(1), Definition(2).
            let keys = vec![
                Value::Int(schema_id),
                Value::String(Rc::new(name.name.to_string())),
            ];
            if let Some((pp, off)) = t.ix_get(db, keys, 0) {
                let p = &mut *pp.borrow_mut();
                let off = off + t.info.off[2];
                let (val, oldcode) = Value::load(db, BIGSTR, &p.data, off);
                if val.str() != source {
                    db.delcode(oldcode);
                    let val = Value::String(source);
                    let newcode = db.encode(&val, data_size(BIGSTR));
                    val.save(BIGSTR, &mut p.data, off, newcode);
                    t.file.set_dirty(p, &pp);
                    db.function_reset.set(true);
                }
                return;
            }
            panic!("function {} not found", &name.str());
        } else {
            if get_function_id(db, name).is_some() {
                panic!("function already exists");
            }
            // Create new function.
            let mut row = t.row();
            // Columns are Schema, Name, Definition
            row.id = t.alloc_id(db);
            row.values[0] = Value::Int(schema_id);
            row.values[1] = Value::String(Rc::new(name.name.clone()));
            row.values[2] = Value::String(source);
            t.insert(db, &mut row);
        }
    } else {
        panic!("schema [{}] not found", &name.schema);
    }
}

/// Get the id of a schema from a name.
pub fn get_schema(db: &DB, sname: &str) -> Option<i64> {
    if let Some(&id) = db.schemas.borrow().get(sname) {
        return Some(id);
    }
    let t = &db.sys_schema;
    let keys = vec![Value::String(Rc::new(sname.to_string()))];
    if let Some((pp, off)) = t.ix_get(db, keys, 0) {
        let p = &pp.borrow();
        let a = t.access(p, off);
        debug_assert!(a.str(db, 0) == sname);
        let id = a.id() as i64;
        db.schemas.borrow_mut().insert(sname.to_string(), id);
        return Some(id);
    }
    None
}

/// Get the id, root, id_gen for specified table.
fn get_table0(db: &DB, name: &ObjRef) -> Option<(i64, i64, i64)> {
    if let Some(schema_id) = get_schema(db, &name.schema) {
        let t = &db.sys_table;
        // Columns are root, schema, name, id_gen
        let keys = vec![
            Value::Int(schema_id),
            Value::String(Rc::new(name.name.to_string())),
        ];
        if let Some((pp, off)) = t.ix_get(db, keys, 0) {
            let p = &pp.borrow();
            let a = t.access(p, off);
            return Some((a.id() as i64, a.int(0), a.int(3)));
        }
    }
    None
}

/// Get information about an index from name.
pub fn get_index(db: &DB, tname: &ObjRef, iname: &str) -> (Rc<Table>, usize, u64) {
    if let Some(t) = get_table(db, tname) {
        // Loop through indexes. Columns are Root, Table, Name.
        let ixt = &db.sys_index;
        let key = Value::Int(t.id);
        for (ix, (pp, off)) in ixt.scan_key(db, key, 0).enumerate() {
            let p = &pp.borrow();
            let a = ixt.access(p, off);
            if a.str(db, 2) == iname {
                let id = a.id();
                return (t, ix, id);
            }
        }
        panic!("index {} not found", iname);
    } else {
        panic!("table {} not found", tname.str());
    }
}

/// Gets table from the database.
pub fn get_table(db: &DB, name: &ObjRef) -> Option<Rc<Table>> {
    if let Some((table_id, root, id_gen)) = get_table0(db, name) {
        let mut info = ColInfo::empty(name.clone());
        // Get columns. Columns are Table, Name, Type
        let t = &db.sys_column;
        let key = Value::Int(table_id);
        for (pp, off) in t.scan_key(db, key, 0) {
            let p = &pp.borrow();
            let a = t.access(p, off);
            debug_assert!(a.int(0) == table_id);
            let cname = a.str(db, 1);
            let ctype = a.int(2) as DataType;
            info.add(cname, ctype);
        }
        let table = Table::new(table_id, root as u64, id_gen, Rc::new(info));
        // Get indexes. Columns are Root, Table, Name.
        let t = &db.sys_index;
        let key = Value::Int(table_id);
        for (pp, off) in t.scan_key(db, key, 0) {
            let p = &pp.borrow();
            let a = t.access(p, off);
            debug_assert!(a.int(1) == table_id);
            let index_id = a.id() as i64;
            let root = a.int(0) as u64;
            let mut cols = Vec::new();
            let t = &db.sys_index_col;
            // Columns are Index, ColIndex
            let key = Value::Int(index_id);
            for (pp, off) in t.scan_key(db, key, 0) {
                let p = &pp.borrow();
                let a = t.access(p, off);
                debug_assert!(a.int(0) == index_id);
                let cnum = a.int(1) as usize;
                cols.push(cnum);
            }
            table.add_index(root, cols, index_id);
        }
        db.publish_table(table.clone());
        Some(table)
    } else {
        None
    }
}

/// Get then parse a function from the database.
pub fn get_function(db: &DB, name: &ObjRef) -> Option<Rc<Function>> {
    if let Some(schema_id) = get_schema(db, &name.schema) {
        let t = &db.sys_function;
        let keys = vec![
            Value::Int(schema_id),
            Value::String(Rc::new(name.name.to_string())),
        ];
        if let Some((pp, off)) = t.ix_get(db, keys, 0) {
            let p = &pp.borrow();
            let a = t.access(p, off);
            let source = Rc::new(a.str(db, 2));
            let function = parse_function(db, source);
            db.functions
                .borrow_mut()
                .insert(name.clone(), function.clone());
            return Some(function);
        }
    }
    None
}

/// Get the id of a function.
pub fn get_function_id(db: &DB, name: &ObjRef) -> Option<i64> {
    if let Some(schema_id) = get_schema(db, &name.schema) {
        let t = &db.sys_function;
        let keys = vec![
            Value::Int(schema_id),
            Value::String(Rc::new(name.name.to_string())),
        ];
        if let Some((pp, off)) = t.ix_get(db, keys, 0) {
            let p = &pp.borrow();
            let a = t.access(p, off);
            return Some(a.id() as i64);
        }
    }
    None
}

/// Parse a function definition.
fn parse_function(db: &DB, source: Rc<String>) -> Rc<Function> {
    let mut p = Parser::new(&source, db);
    p.b.parse_only = true;
    p.parse_function();
    Rc::new(Function {
        compiled: Cell::new(false),
        ilist: RefCell::new(Vec::new()),
        local_typ: p.b.local_typ,
        return_type: p.b.return_type,
        param_count: p.b.param_count,
        source,
    })
}

/// Get the IdGen field for a table. This is only needed to initialise system tables.
pub fn get_id_gen(db: &DB, id: u64) -> i64 {
    let t = &db.sys_table;
    let (pp, off) = t.id_get(db, id).unwrap();
    let p = &pp.borrow();
    let a = t.access(p, off);
    debug_assert!(a.id() == id);
    a.int(3)
}

/// Update IdGen field for a table.
pub fn save_id_gen(db: &DB, id: u64, val: i64) {
    let t = &db.sys_table;
    let (pp, off) = t.id_get(db, id).unwrap();
    let p = &mut pp.borrow_mut();
    let mut wa = t.write_access(p, off);
    debug_assert!(wa.id() == id);
    wa.set_int(3, val);
    t.file.set_dirty(p, &pp);
}

/// Update root page for table ( for ALTER TABLE ).
pub fn set_root(db: &DB, id: i64, new_root: u64) {
    let id = id as u64;
    let t = &db.sys_table;
    let (pp, off) = t.id_get(db, id).unwrap();
    let p = &mut pp.borrow_mut();
    let mut wa = t.write_access(p, off);
    debug_assert!(wa.id() == id);
    wa.set_int(0, new_root as i64);
    t.file.set_dirty(p, &pp);
}

/// Update root page for index.
#[cfg(feature = "renumber")]
pub fn set_ix_root(db: &DB, id: i64, new_root: u64) {
    let id = id as u64;
    let t = &db.sys_index;
    let (pp, off) = t.id_get(db, id).unwrap();
    let p = &mut pp.borrow_mut();
    let mut wa = t.write_access(p, off);
    debug_assert!(wa.id() == id);
    wa.set_int(0, new_root as i64);
    t.file.set_dirty(p, &pp);
}