|
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!=, orswitchconditions), values of different types might be equal.For
integervalues, zero is a special case, in particular the following results might be unexpected: