-
Notifications
You must be signed in to change notification settings - Fork 2
/
closure_table.py
104 lines (85 loc) · 4.18 KB
/
closure_table.py
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
import sqlite3
class ClosureTable:
def __init__(self, conn):
self.connexion = conn
self.cursor = self.build_schema(conn)
def build_schema(self, conn):
cursor = conn.cursor()
cursor.executescript('''
DROP TABLE IF EXISTS data_table;
DROP TABLE IF EXISTS tree;
-- Our data
CREATE TABLE data_table
(id INTEGER PRIMARY KEY,
updated DATE NOT NULL,
version INTEGER NOT NULL DEFAULT 1,
dummy INTEGER);
-- Closure table that keeps track of the tree
CREATE TABLE tree(
parent INTEGER NOT NULL DEFAULT 0,
child INTEGER NOT NULL DEFAULT 0,
depth INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY (parent, child),
FOREIGN KEY(parent) REFERENCES data_table(id),
FOREIGN KEY(child) REFERENCES data_table(id));
CREATE UNIQUE INDEX tree_idx ON tree(parent, depth, child);
CREATE UNIQUE INDEX tree_idx2 ON tree(child, parent, depth);
''')
return cursor
# return the depth of the tree between the root and this node
def ancestors_depth(self, rownum):
result = self.cursor.execute('SELECT MAX(depth) FROM tree WHERE child = ?', (rownum,));
return result.fetchone()[0]
# return the depth of the subtree under the node
def descendants_depth(self, rownum):
self.cursor.execute('SELECT MAX(depth) FROM tree WHERE parent = ?', (rownum,));
return self.cursor.fetchone()[0]
# return the whole subtree at this node
def select_descendants(self, rownum):
self.cursor.execute('''
SELECT dta.* FROM data_table dta
JOIN tree t ON (dta.id = t.child) WHERE t.parent = ? AND depth > 0
ORDER BY depth ASC''', (rownum,))
return self.cursor.fetchall()
# return the ancestors of the node
def select_ancestors(self, rownum):
self.cursor.execute('''
SELECT dta.* FROM data_table dta
JOIN tree t ON (dta.id = t.parent) WHERE t.child = ? AND depth > 0
ORDER BY depth DESC''', (rownum,))
return self.cursor.fetchall()
# return the immediate parent node
def select_parent(self, rownum):
self.cursor.execute('''
SELECT dta.* FROM data_table dta
JOIN tree t ON (dta.id = t.parent) WHERE t.child = ? AND depth = 1''', (rownum,))
return self.cursor.fetchone()
# return the immediate children
def select_children(self, rownum):
self.cursor.execute('''
SELECT dta.* FROM data_table dta
JOIN tree t ON (dta.id = t.child) WHERE t.parent = ? AND depth = 1''', (rownum,))
return self.cursor.fetchall()
# insert the root node
def insert_root(self, row_parent):
self.cursor.execute('INSERT INTO tree(parent, child, depth) VALUES (?,?,0)', (row_parent, row_parent))
# add a new child to a node
def insert_child(self, row_parent, row_child):
self.cursor.execute('INSERT INTO tree(parent, child, depth) VALUES (?,?,0)', (row_child, row_child))
self.link_child(row_parent, row_child)
# link a child node/subtree to a new parent
def link_child(self, row_parent, row_child):
self.cursor.execute('''
INSERT OR REPLACE INTO tree(parent, child, depth)
SELECT p.parent, c.child, p.depth + c.depth + 1
FROM tree p, tree c
WHERE p.child = ? AND c.parent = ?''', (row_parent, row_child))
# unlink a node from its parent
def unlink_child(self, row_child):
self.cursor.execute('DELETE FROM tree WHERE child = ? AND depth = 1', (row_child,))
# unlink a parent node from its child
def unlink_parent(self, rownum):
self.cursor.execute('DELETE FROM tree WHERE parent = ? AND depth = 1', (rownum,))
# delete a subtree
def delete_descendants(self, rownum):
self.cursor.execute('DELETE FROM tree WHERE parent = ? AND child <> ?', (rownum, rownum))