This page provides a series of declarations and complex updates that tests the correctness and explains our TR implementation. The tutorial consists of 11 parts:
|?- declare p/2 key 2. yes |
declare p/2 with the whole tuple as the key. |
|?- trans create(p(1,a)) sc create(p(2,b)). yes |
|
?- p(X,Y).
X = 1
X = 2 no |
|
|?- declare q/2 key 1. yes |
declare q/2 with the first argument as the key. |
|?- trans create(q(1,a)) sc create(q(2,b)). yes |
|
|?- q(X,Y).
X = 1
X = 2 no |
|
|?- declare r/1. yes |
declare r/1. No key field is indicated in the transaction, so the whole tuple is used as the key. |
|?- trans create r(1) sc create r(2). yes |
|
|?- r(X). X = 1; X = 2; no |
|
|?- trans create(p(1,c)). yes |
p(1,c) successfully created. |
|?- trans create(q(1,c)). no |
create q(1,c) failed because the first argument is the key and q(1,a) is already in the database. |
?- q(1,X). X = a; no |
q(1,c) is not in the database. |
File 1:
a(X,Y) <- p(X,Y) sc r(X). |
File 2:
declare q/2 key 1. |
|?- loadbase(file2). yes |
|
|?- loadtrans(file1). yes |
Base predicates in the transaction base do not require declare statement because they are derived, not stored, and are not restricted by the key of database predicate with same name and number of arguments. |
|?- r(X). X = 2; no |
A database query considers data in database only. |
|?- trans r(X). X = 2; X = 1; no |
A transaction query considers data in both database and transaction base. |
|?- b(X,Y).
X = 2 no |
A database rule accesses predicate in the database only. |
|?- trans b(X,Y).
X = 2 no |
Even in a transaction query, database rule still only consider database items only. So, r(1) in the transaction base is not considered by in the database rule. |
|?- a(X,Y). no |
Transaction rules are invisible to database queries. |
|?- trans a(X,Y).
X = 2
X = 1
X = 2 no |
A transaction rule considers data in both transaction base and database. |
|?- trans destroy(r(4)). no |
Destroying q predicate in the transaction base is not allowed. |
|?- trans destroy(s(4)). Undefined predicate/function: destroy_1_r/1 no |
The destory operation is defined only on tuples declared in the database. Since no tuple about s is declared in the database, the destroy operation is undefined here. |
|?- trans destroy(p(1,c)). yes |
removes p(1,c) from database |
|?- p(X,Y).
X = 1
X = 2 no |
Now, p(1,a) and p(2,b) are left in database. |
|?- trans destroy(p(X,Y)).
X = 1
X = 2 yes |
We can select an atom to delete. First, p(X,Y) is unified to p(1,a), and delete it. Then, the deletion rolled back and delete p(2,b). |
|?- p(X,Y).
X = 1 no |
p(2,b) was deleted, but p(1,a) was not. |
|?- trans destroy(q(X,Y)) sc write('aBc ') sc fail. aBc aBc aBc no |
The execution deletes an atom from q(X,Y), then printed the word ``aBc'', and then forced to fail. So, the number of times ``aBc'' shown indicated the number of times destroy(q(X,Y)) succeeded which is the same as number of backtracking, or the number of atoms of q(X,Y). Three ``aBc'' were shown, so q(X,Y) has three tuples. |
?- q(X,Y).
X = 1
X = 2
X = 3 no |
The extent and order of q(X,Y) was perserved. |
|?- trans destroy(q(X,Y)) sc destroy(r(Z)) sc write('aBc ') sc fail. aBc aBc aBc aBc aBc aBc aBc aBc aBc no |
There are 3*3 choices for backtracking p and q, i.e., (X,Y,Z) are (1,a,1), (1,a,2), (1,a,3), (2,b,1), (2,b,2), (2,b,3 (3,a,1), (3,a,3), (3,a,3). So, 9 ``aBc'' are shown. |
|?- trans set q(1,b). yes |
change the value of q(1,a) to q(1,b) |
?- q(X,Y).
X = 1
X = 2
X = 3 no |
|
|?- trans set q(4,a). no |
'set' is not equivalent to 'create'. If the key is not already in the database, then 'set' fails. |
|?- trans set p(1,b). Undefined predicate/function: set_2_p/2 no |
'set' is not defined on p/2 because the whole predicate is the key (i.e., there are no non-key attributes whose values can be set). |
|?- trans bulk\_ins(p(X,Y),r(X)).
X = _1264924 yes |
bulk insertion of p(X,Y) into r(X), so r(1), r(2) and r(3) are added to the database. Also, it copies data from the database only. |
|?- r(X). X = 4; X = 1; X = 2; X = 3; no |
|
|?- trans bulk_ins(p(X,Y),q(X,Y)) sc fail. no |
Bulk insertion into q forced to fail and backtrack, so q(X,Y) should still contains no tuples. |
|?- q(X,Y). no |
|
|?- trans bulk_del(p(X,a)). X = _1264776 yes |
bulk deletion of p(X,a), so p(1,a) and p(3,a) should be deleted. |
|?- p(X,Y).
X = 2 no |
p(2,b) should be the only p-tuple left. |
|?- trans bulk\_del(p(X,Y)) sc fail. no |
A bulk deletion forced to fail and backtrack, so p(2,b) is still in the database after the execution. |
|?- p(X,Y).
X = 2 no |
|?- trans q(X,Y) := p(X,Y).
X = _1267740 yes |
Since q/2 has the first argument as key, the assignment cannot copy both q(1,a) and q(1,b) to the database, so one of them is chosen non-deterministically. |
?- q(X,Y).
X = 1
X = 2 no |
Also, p(1,1) in the transaction base has no effect on the assignment. |
|?- trans q(X,Y) := (p(X,Y), r(Y)).
X = _1268180 yes |
This test shows the use of a database formula (q(X,Y),r(Y)) rather than a single query in assignment. Since r has only one tuple in the database, i.e., r(b), Y is bound to b, and so p(1,b) and p(2,b) are satisfied. |
|?- q(X,Y).
X = 1
X = 2 no |
q(1,b) and q(2,b) were inserted. |
Case 1:
|?- trans Y = a sc while p(X,Y) do destroy(p(X,Y)).
Y = a yes |
Although Y is bound to a, inside the while loop, it is still considered to be a free variable. So, p is empty after the execution. |
|?- p(X,Y). no |
|
|?- trans p(X,Y).
X = 4 yes |
p(4,4) is still in transaction base. |
Case 2:
|?- trans Y = a sc with [Y] while p(X,Y) do destroy(p(X,Y)).
Y = a yes |
The term ``with [Y]'' in the loop causes Y to be imported into the loop. Since Y is bound to 1, database atoms of the form p(X,1) are destroyed during the execution of the loop. |
|?- p(X,Y).
X = 2 no |
Case 3:
|?- trans Y = a sc forall [X,Y] in p(X,Y) do create(s(X,Y)).
Y = a yes |
As in case 1 above, Y is unified to a, but it is still a free variable inside the for-loop. So, the loop iterates on all pairs X,Y. So, when the loop terminates, s will contain the same information as p. |
|?- s(X,Y).
X = 2
X = 1
X = 3 no |
X,Y unified with data in database only, so s(4,4) was not created. |
Case 4:
|?- trans Y = a sc with [Y] forall [X,Y] in p(X,Y) do create(s(X,Y)).
Y = a yes |
As in case 2 above, Y = a is imported into the for loop, so X binds to 1 and 3 during loop iteration, and s(1,a), s(3,a) are created. |
|?- s(X,Y).
X = 1
X = 3 no |
|?- trans forall [Y] in p(X,Y) do write('aBc '). aBc aBc aBc Y = _1264920 X = _1264964 yes |
Y = a,b,a are chosen | |
|?- trans forall unique [Y] in p(X,Y) do write('aBc '). aBc aBc Y = _1264936 X = _1264980 yes |
Y = a,b are chosen | |
|?- trans forall possible [Y] in p(X,Y) do write('aBc '). aBc aBc Y = _1264936 X = _1264980 yes |
Y = a,b are chosen | |
|?- trans forall [X,Y] in p(X,Y) do write('ac ') sc write('l ') sc fail. ac ac ac l no |
Forced failure of for loop but no attemp to re-execute it in a different way. | |
|?- trans forall unique [X,Y] in p(X,Y) do write('ac ') sc write('l ') sc fail. ac ac ac l no |
Forced failure of for loop but no attemp to re-execute it in a different way | |
|?- trans forall possible [X,Y] in p(X,Y) do write('ac ') sc write('l ') sc fail. ac ac ac l ac ac l ac ac ac l ac ac l ac ac ac l ac ac l no |
Loop passed sixth times (six '1' printed). Note that sometimes there are 3 iterations to execute a loop, but sometimes there are only two, because the transaction does not backtrack to the first iteration. For example, the order p(X,Y) in the first succeeded loop is p(1,a), p(2,b), p(3,a). When it was forced to fail, it backtracks and the second and the third iteration only, i.e., exchange the order to p(1,a), p(3,a), p(2,b). So, only 2 'ac' printed in the second loop. In the next backtracking, the iteration backtracks to the first iteration, and the order is p(2,b), p(1,a), p(3,a). Eventually, all possible combinations are chosen. |
|?- trans while p(X,Y) do destroy(p(X,Y)) sc write('aBc ') sc fail. aBc aBc aBc aBc aBc aBc no |
Loop passed six times because there are 6 different orders to destroy p(X,Y). |
|?- p(X,Y).
X = 1
X = 2
X = 3 no |
p(X,Y) unchanged. |
|?- trans forall [X,Y] in p(X,Y) do destroy(p(X,Y)) sc fail. no |
The for loop forced to fail. So, the destroy operations should be undone. |
|?- p(X,Y).
X = 1
X = 2
X = 3 no |
p(X,Y) unchanged |
|?- trans q(X) sc if p(X,Y) then write(Y) else write(X). a X = 1 Y = a; b X = 2 Y = b; no |
X unifies to 1 first, then the condition p(1,Y) succeeds, unifying Y to a in the then-part, which writes a. The transaction backtracks to unify X to 2 and Y to b. |
|?- trans q(X) sc if p(X,Y) then write(Y). a X = 1 Y = a; b X = 2 Y = b; no |
Since in the above test, the else-part was not used, we can drop the else-part and obtain same result. |
|?- trans if p(1,1) then write(then) else write(else). else yes |
Since p(1,1) is not in the database, else-part is executed. |
|?- trans if p(1,1) then write(then). yes |
If the condition fails, an if-then formula is still true. |
Case 1:
|?- trans forall [X] in q(X) do (with [X] while p(X,Y) do destroy(p(X,Y))).
X = _1265480 yes |
|?- p(X,Y).
X = 3 no |
In the forall loop, X is successfully unified to 1 and 2. So, all tuples of the form p(1,Y) and p(2,Y) are eventually deleted, leaving p(3,3) in database. |
Case 2:
|?- trans while q(X) do (write(X) sc with [X] forall [Y] in p(X,Y) do destroy(q(Y))). 122 X = _1265488 Y = _1265564 yes |
|?- q(X). no |
Explanation : Normally, this execution should fail because in the first iteration of the outer loop, X is bound to 1, and q(1) is destroyed in the forall loop. In the next iteration of the outer loop, X is bound to 2. The inner loop becomes forall [Y] in p(2,Y) do destroy(q(Y)), and Y is unified to 1 and 2 in the iterations. The iteration fails because destroy(q(1)) fails in the execution of this inner loop. Therefore, the program backtracks and chooses other order to executing the outer while loop. During backtracking of the outer loop, the program chooses to bind X to 2 first. Then, both q(1) and q(2) are destroyed in the forall loop. Since there is no q(X) left in the database, the while loop succeeds. |
Database contains: p(1,a), p(1,b), p(1,2), p(2,a).
Transaction base contains the rule:
a(X) <- with [X] forall [Y] in p(X,Y) do (write(Y) sc a(Y)).
|?- trans a(1). ab2a yes |
Explanation : During execution of the loop, Y is unified successively to a, b, and 2. In the first iteration, a is printed and a(a) is called, i.e., forall [Y] in p(a,Y) do destroy(q(Y)). Since there is no p(a,Y) in database, the loop succeeds without any iteration. In the second iteration, b is printed and a(b) is called. Similar, there is no p(b,Y) in the database, and so a(b) succeeds without any updates to database. In the third iteration, 2 is printed and a(2) is called. Since p(2,a) is in database, the loop iterates, binding Y to a, so a is printed. |