1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* This file is part of the sweetrdf/InMemoryStoreSqlite package and licensed under |
5
|
|
|
* the terms of the GPL-2 license. |
6
|
|
|
* |
7
|
|
|
* (c) Konrad Abicht <[email protected]> |
8
|
|
|
* (c) Benjamin Nowack |
9
|
|
|
* |
10
|
|
|
* For the full copyright and license information, please view the LICENSE |
11
|
|
|
* file that was distributed with this source code. |
12
|
|
|
*/ |
13
|
|
|
|
14
|
|
|
namespace sweetrdf\InMemoryStoreSqlite\Store\QueryHandler; |
15
|
|
|
|
16
|
|
|
use Exception; |
17
|
|
|
|
18
|
|
|
class DeleteQueryHandler extends QueryHandler |
19
|
|
|
{ |
20
|
|
|
private array $infos; |
21
|
|
|
|
22
|
|
|
private bool $refs_deleted; |
23
|
|
|
|
24
|
8 |
|
public function runQuery($infos) |
25
|
|
|
{ |
26
|
8 |
|
$this->infos = $infos; |
27
|
|
|
/* delete */ |
28
|
8 |
|
$this->refs_deleted = false; |
29
|
|
|
/* graph(s) only */ |
30
|
8 |
|
$constructTriples = $this->infos['query']['construct_triples'] ?? []; |
31
|
8 |
|
$pattern = $this->infos['query']['pattern'] ?? []; |
32
|
8 |
|
if (!$constructTriples) { |
33
|
4 |
|
$tc = $this->deleteTargetGraphs(); |
34
|
4 |
|
} elseif (!$pattern) { |
35
|
|
|
/* graph(s) + explicit triples */ |
36
|
2 |
|
$tc = $this->deleteTriples(); |
37
|
|
|
} else { |
38
|
|
|
/* graph(s) + constructed triples */ |
39
|
2 |
|
$tc = $this->deleteConstructedGraph(); |
40
|
|
|
} |
41
|
|
|
/* clean up */ |
42
|
8 |
|
if ($tc && ($this->refs_deleted || (1 == rand(1, 100)))) { |
|
|
|
|
43
|
4 |
|
$this->cleanTableReferences(); |
44
|
|
|
} |
45
|
|
|
|
46
|
8 |
|
return [ |
47
|
8 |
|
't_count' => $tc, |
48
|
8 |
|
'delete_time' => 0, |
49
|
8 |
|
'index_update_time' => 0, |
50
|
8 |
|
]; |
51
|
|
|
} |
52
|
|
|
|
53
|
4 |
|
private function deleteTargetGraphs() |
54
|
|
|
{ |
55
|
4 |
|
$r = 0; |
56
|
4 |
|
foreach ($this->infos['query']['target_graphs'] as $g) { |
57
|
4 |
|
if ($g_id = $this->getTermID($g, 'g')) { |
58
|
4 |
|
$r += $this->store->getDBObject()->exec('DELETE FROM g2t WHERE g = '.$g_id); |
59
|
|
|
} |
60
|
|
|
} |
61
|
4 |
|
$this->refs_deleted = $r ? 1 : 0; |
62
|
|
|
|
63
|
4 |
|
return $r; |
64
|
|
|
} |
65
|
|
|
|
66
|
4 |
|
private function deleteTriples() |
67
|
|
|
{ |
68
|
4 |
|
$r = 0; |
69
|
|
|
/* graph restriction */ |
70
|
4 |
|
$tgs = $this->infos['query']['target_graphs']; |
71
|
4 |
|
$gq = ''; |
72
|
4 |
|
foreach ($tgs as $g) { |
73
|
1 |
|
if ($g_id = $this->getTermID($g, 'g')) { |
74
|
1 |
|
$gq .= $gq ? ', '.$g_id : $g_id; |
75
|
|
|
} |
76
|
|
|
} |
77
|
4 |
|
$gq = $gq ? ' AND G.g IN ('.$gq.')' : ''; |
78
|
|
|
/* triples */ |
79
|
4 |
|
foreach ($this->infos['query']['construct_triples'] as $t) { |
80
|
4 |
|
$q = ''; |
81
|
4 |
|
$skip = 0; |
82
|
4 |
|
foreach (['s', 'p', 'o'] as $term) { |
83
|
4 |
|
if (isset($t[$term.'_type']) && preg_match('/(var)/', $t[$term.'_type'])) { |
84
|
|
|
//$skip = 1; |
85
|
|
|
} else { |
86
|
4 |
|
$term_id = $this->getTermID($t[$term], $term); |
87
|
4 |
|
$q .= ($q ? ' AND ' : '').'T.'.$term.'='.$term_id; |
88
|
|
|
/* explicit lang/dt restricts the matching */ |
89
|
4 |
|
if ('o' == $term) { |
90
|
2 |
|
$o_lang = $t['o_lang'] ?? ''; |
91
|
2 |
|
$o_lang_dt = $t['o_datatype'] ?? $o_lang; |
92
|
2 |
|
if ($o_lang_dt) { |
93
|
|
|
$q .= ($q ? ' AND ' : '').'T.o_lang_dt='.$this->getTermID($o_lang_dt, 'lang_dt'); |
94
|
|
|
} |
95
|
|
|
} |
96
|
|
|
} |
97
|
|
|
} |
98
|
4 |
|
if ($skip) { |
99
|
|
|
continue; |
100
|
|
|
} |
101
|
4 |
|
if ($gq) { |
102
|
1 |
|
$sql = 'DELETE FROM g2t WHERE t IN ('; |
103
|
1 |
|
$sql .= 'SELECT G.t FROM g2t G JOIN triple T ON T.t = G.t'.$gq.' WHERE '.$q; |
104
|
1 |
|
$sql .= ')'; |
105
|
|
|
} else {/* triples only */ |
106
|
|
|
// it contains things like "T.s", but we can't use a table alias |
107
|
|
|
// with SQLite when running DELETE queries. |
108
|
3 |
|
$q = str_replace('T.', '', $q); |
109
|
3 |
|
$sql = 'DELETE FROM triple WHERE '.$q; |
110
|
|
|
} |
111
|
4 |
|
$r += $this->store->getDBObject()->exec($sql); |
112
|
4 |
|
if (!empty($this->store->getDBObject()->getErrorMessage())) { |
113
|
|
|
// TODO deletable because never reachable? |
114
|
|
|
throw new Exception($this->store->getDBObject()->getErrorMessage().' in '.$sql); |
115
|
|
|
} |
116
|
|
|
} |
117
|
|
|
|
118
|
4 |
|
return $r; |
119
|
|
|
} |
120
|
|
|
|
121
|
2 |
|
private function deleteConstructedGraph() |
122
|
|
|
{ |
123
|
2 |
|
$subLogger = $this->store->getLoggerPool()->createNewLogger('Construct'); |
124
|
2 |
|
$h = new ConstructQueryHandler($this->store, $subLogger); |
125
|
|
|
|
126
|
2 |
|
$sub_r = $h->runQuery($this->infos); |
127
|
2 |
|
$triples = $this->getTriplesFromIndex($sub_r); |
128
|
2 |
|
$tgs = $this->infos['query']['target_graphs']; |
129
|
|
|
|
130
|
2 |
|
$this->infos = ['query' => ['construct_triples' => $triples, 'target_graphs' => $tgs]]; |
131
|
|
|
|
132
|
2 |
|
return $this->deleteTriples(); |
133
|
|
|
} |
134
|
|
|
|
135
|
2 |
|
private function getTriplesFromIndex(array $index): array |
136
|
|
|
{ |
137
|
2 |
|
$r = []; |
138
|
2 |
|
foreach ($index as $s => $ps) { |
139
|
2 |
|
foreach ($ps as $p => $os) { |
140
|
2 |
|
foreach ($os as $o) { |
141
|
2 |
|
$r[] = [ |
142
|
2 |
|
's' => $s, |
143
|
2 |
|
'p' => $p, |
144
|
2 |
|
'o' => $o['value'], |
145
|
2 |
|
's_type' => preg_match('/^\_\:/', $s) ? 'bnode' : 'uri', |
146
|
2 |
|
'o_type' => $o['type'], |
147
|
2 |
|
'o_datatype' => isset($o['datatype']) ? $o['datatype'] : '', |
148
|
2 |
|
'o_lang' => isset($o['lang']) ? $o['lang'] : '', |
149
|
2 |
|
]; |
150
|
|
|
} |
151
|
|
|
} |
152
|
|
|
} |
153
|
|
|
|
154
|
2 |
|
return $r; |
155
|
|
|
} |
156
|
|
|
|
157
|
4 |
|
private function cleanTableReferences() |
158
|
|
|
{ |
159
|
|
|
/* check for unconnected triples */ |
160
|
4 |
|
$sql = 'SELECT T.t |
161
|
|
|
FROM triple T LEFT JOIN g2t G ON ( G.t = T.t ) |
162
|
|
|
WHERE G.t IS NULL |
163
|
4 |
|
LIMIT 1'; |
164
|
|
|
|
165
|
4 |
|
$numRows = $this->store->getDBObject()->getNumberOfRows($sql); |
166
|
|
|
|
167
|
4 |
|
if (0 < $numRows) { |
168
|
|
|
/* delete unconnected triples */ |
169
|
3 |
|
$sql = 'DELETE FROM triple WHERE t IN ('; |
170
|
3 |
|
$sql .= ' SELECT T.t |
171
|
|
|
FROM triple T |
172
|
|
|
LEFT JOIN g2t G ON G.t = T.t |
173
|
3 |
|
WHERE G.t IS NULL'; |
174
|
3 |
|
$sql .= ')'; |
175
|
3 |
|
$this->store->getDBObject()->exec($sql); |
176
|
|
|
} |
177
|
|
|
/* check for unconnected graph refs */ |
178
|
4 |
|
if ((1 == rand(1, 10))) { |
179
|
1 |
|
$sql = ' |
180
|
|
|
SELECT G.g FROM g2t G LEFT JOIN triple T ON ( T.t = G.t ) |
181
|
|
|
WHERE T.t IS NULL LIMIT 1 |
182
|
1 |
|
'; |
183
|
1 |
|
if (0 < $this->store->getDBObject()->getNumberOfRows($sql)) { |
184
|
|
|
/* delete unconnected graph refs */ |
185
|
|
|
$sql = 'DELETE G |
186
|
|
|
FROM g2t G |
187
|
|
|
LEFT JOIN triple T ON (T.t = G.t) |
188
|
|
|
WHERE T.t IS NULL |
189
|
|
|
'; |
190
|
|
|
$this->store->getDBObject()->exec($sql); |
191
|
|
|
} |
192
|
|
|
} |
193
|
|
|
} |
194
|
|
|
} |
195
|
|
|
|
In PHP, under loose comparison (like
==
, or!=
, orswitch
conditions), values of different types might be equal.For
integer
values, zero is a special case, in particular the following results might be unexpected: