1 package body ojds_context as
2 -- make a new context and return its node
3 function mkcontext (p number, c varchar2) return number is
4 inode number;
5 begin
6 select ojds$node_number$.nextval into inode from dual;
7 insert into ojds$inode$ (node, type, refcount, creation_ts,
8 last_modified, owner, is_context)
9 values (inode, 1, 0, sysdate, sysdate, c, 1);
10
11 -- "." and ".." do not contribute to reference counting
12 -- that's why the links are inserted and deleted manually
13 -- bind ".." to the parent
14 insert into ojds$bindings$ (parent, child, id, binding_type)
15 values (inode, p, '..', 1);
16 -- bind "." to the parent
17 insert into ojds$bindings$ (parent, child, id, binding_type)
18 values (inode, inode, '.', 1);
19 return inode;
20 end;
21
22 -- make a new reference and return its node
23 function mkreference (r varchar2) return number is
24 inode number;
25 begin
26 select ojds$node_number$.nextval into inode from dual;
27 insert into ojds$inode$ (node, type, refcount, creation_ts,
28 last_modified, owner, is_context)
29 values (inode, 2, 0, sysdate, sysdate, r, 0);
30 return inode;
31 end;
32
33 -- bind a node to a parent
34 procedure link (p number, c number, i varchar2 , bt number) is
35 begin
36 -- update the refcount first, as we should be idempotent on
37 -- ref managment.
38 update ojds$inode$ set refcount = refcount + 1 where node = c;
39 insert into ojds$bindings$ (parent, child, id, binding_type)
40 values (p, c, i, bt);
41 end;
42
43 -- remove a node from a parent
44 procedure unlink (p number, i varchar2) is
45 c number;
46 bt number;
47 begin
48 -- gets the child and lock the rows in bindings$ and inode$ tables
49 select b.child, b.binding_type into c, bt
50 from ojds$bindings$ b, ojds$inode$ n
51 where b.parent = p and b.id = i and
52 n.node = b.child for update;
53 -- delete "." and ".." if it's a context
54 if bt = 1 then
55 delete from ojds$bindings$ where parent = c and (id = '.' or id = '..');
56 end if;
57 -- delete the parent -> child link
58 delete from ojds$bindings$
59 where parent = p and id = i;
60 -- update the child ref count
61 update ojds$inode$ set refcount = refcount - 1 where node = c;
62 -- delete the child if refcount is 0
63 delete from ojds$inode$ where refcount = 0 and node = c;
64 -- if child was deleted also delete its entries in $permissions
65 if sql%rowcount > 0 then -- true if the node was deleted in $inode
66 delete from ojds$permissions$ where node = c;
67 delete from ojds$attributes$ where node = c;
68 delete from ojds$refaddr$ where node = c;
69 end if;
70 exception
71 when others then -- catches the case of deleting an unknown binding
72 if sql%notfound then
73 null;
74 end if;
75 end;
76
77 -- remove unreferenced nodes
78 function rmUnrefNodes (junk number) return boolean is
79 cursor unrefNodes_cur is select node from ojds$inode$ where refcount = 0;
80 notEmpty boolean;
81 begin
82 notEmpty := FALSE;
83 for unrefNodes_rec in unrefNodes_cur
84 loop
85 notEmpty := TRUE;
86 -- delete the inode, permission, and attributes
87 delete from ojds$inode$ where node = unrefNodes_rec.node;
88 delete from ojds$permissions$ where node = unrefNodes_rec.node;
89 delete from ojds$attributes$ where node = unrefNodes_rec.node;
90 delete from ojds$refaddr$ where node = unrefNodes_rec.node;
91 -- delete child bindings and children's refcount
92 update ojds$inode$ set refcount = refcount - 1 where
93 node in (select i.node from ojds$inode$ i, ojds$bindings$ b
94 where i.node = b.child and b.parent = unrefNodes_rec.node and b.id <> '..' and b.id <> '.');
95 delete from ojds$bindings$ where parent = unrefNodes_rec.node;
96 end loop;
97 return notEmpty;
98 end;
99
100 -- remove empty context; return 1 if it's NOT EMPTY; returns 0 : success.
101 function rmEmptyCtx (c number) return number is
102 cnt number;
103 begin
104 select count(*) into cnt from ojds$bindings$ where parent = c and id <> '.' and id <> '..';
105 if (cnt <> 0)
106 then
107 return 1; -- not empty
108 end if;
109 -- delete its own "." and ".."
110 delete from ojds$bindings$ where parent = c;
111 -- delete all pointers to itself
112 delete from ojds$bindings$ where child = c;
113 -- delete the inode, permission, and attributes
114 delete from ojds$inode$ where node = c;
115 delete from ojds$permissions$ where node = c;
116 delete from ojds$attributes$ where node = c;
117 delete from ojds$refaddr$ where node = c;
118 return 0;
119 end;
120
121 -- relink an entry
122 procedure relink (p number, i varchar2, nc number, nt number) is
123 c number;
124 begin
125 -- gets the child and lock the rows in bindings$ and inode$ tables
126 select b.child into c from ojds$bindings$ b, ojds$inode$ n
127 where b.parent = p and b.id = i and
128 n.node = b.child for update;
129 -- update the child ref count
130 update ojds$inode$ set refcount = refcount - 1 where node = c;
131 -- update the binding
132 update ojds$bindings$ set child = nc, binding_type = nt where parent = p and id = i;
133 -- ref managment.
134 update ojds$inode$ set refcount = refcount + 1 where node = nc;
135 -- delete the child if refcount is 0
136 delete from ojds$inode$ where refcount = 0 and node = c;
137 -- if child was deleted also delete its entries in $permissions
138 if sql%rowcount > 0 then -- true if the node was deleted in $inode
139 delete from ojds$permissions$ where node = c;
140 delete from ojds$attributes$ where node = c;
141 delete from ojds$refaddr$ where node = c;
142 end if;
143 exception
144 when others then -- catches the case of deleting an unknown binding
145 if sql%notfound then
146 link(p, nc, i, nt);
147 end if;
148 end;
149
150 -- since permissions are lists, the java code that
151 -- manipulates this deletes all the permissions, and
152 -- adds the new ones since permissions are gc'd with their
153 -- associated inode, this should really be done with
154 -- some sort of constraint or procedure, but I don't know
155 -- how to pass arrays to sql from java.
156 -- helper function for building initial context
157 procedure addperm (i number, t number, s varchar2) is
158 begin
159 insert into ojds$permissions$ (node, type, schema) values (i, t, s);
160 end;
161
162 -- drop user artifacts when a user is dropped.
163 procedure user_dropped (the_user varchar2) is
164 begin
165 drop_inode_s(the_user);
166 drop_permission_s(the_user);
167 end;
168
169 procedure role_dropped (the_role varchar2) is
170 begin
171 drop_permission_s(the_role);
172 end;
173
174 procedure drop_permission_s (the_dropped varchar2) is
175 begin
176 delete from ojds$permissions$ where schema=the_dropped;
177 end;
178
179 procedure drop_inode_s (the_dropped varchar2) is
180 tmpp boolean;
181 begin
182 delete from ojds$permissions$ where schema=the_dropped;
183 -- check inode owner
184 loop
185 tmpp := drop_inode_slow(the_dropped);
186 exit when tmpp = FALSE;
187 end loop;
188 end;
189
190 procedure drop_inode_node (inode_number NUMBER) is
191 begin
192 delete from ojds$inode$ where node = inode_number;
193 delete from ojds$permissions$ where node = inode_number;
194 delete from ojds$attributes$ where node = inode_number;
195 delete from ojds$refaddr$ where node = inode_number;
196 -- delete inward pointers.
197 delete from ojds$bindings$ where child = inode_number;
198 end;
199
200 function is_this_a_context (inode_number NUMBER) return boolean is
201 tmp1 NUMBER;
202 begin
203 select is_context into tmp1 from ojds$inode$ where node = inode_number;
204 if (tmp1 = 0)
205 then
206 return FALSE;
207 else
208 return TRUE;
209 end if;
210 end;
211
212 function drop_inode_slow (the_dropped varchar2) return boolean is
213 cursor drop_inode_cur is select node from ojds$inode$ where owner=the_dropped;
214 notEmpty boolean;
215 inode_number NUMBER;
216 is_context_ NUMBER;
217 begin
218 notEmpty := FALSE;
219 open drop_inode_cur;
220 fetch drop_inode_cur into inode_number;
221 if drop_inode_cur%NOTFOUND then
222 close drop_inode_cur;
223 return FALSE;
224 end if;
225 close drop_inode_cur;
226
227 if (is_this_a_context(inode_number)) then
228 -- take care of '.' and '..'
229 delete from ojds$bindings$ where parent=inode_number and ((id = '.') or (id = '..'));
230 drop_all_child_links(inode_number);
231 end if;
232 drop_inode_node(inode_number);
233 return TRUE;
234 end;
235
236 -- after deleting pointers to children and decrement children's reference count
237 -- we can use 'rmUnrefNode' to take care of the rest
238 procedure drop_all_child_links (inode_number NUMBER) is
239 cursor all_children_cur is select child from ojds$bindings$ where parent = inode_number and id <> '.' and id <> '..';
240 tmpp boolean;
241 begin
242 for children_rec in all_children_cur
243 loop
244 update ojds$inode$ set refcount = refcount - 1 where node = children_rec.child;
245 end loop;
246 delete from ojds$bindings$ where parent=inode_number and id <> '.' and id <> '..';
247 -- remove nodes with zero refcount cascadingly
248 loop
249 tmpp := rmUnrefNodes(1);
250 exit when tmpp = FALSE;
251 end loop;
252 end;
253 end ojds_context;