Passed
Branch extract-store (f24e42)
by Konrad
04:37
created

DeleteQueryHandler::cleanTableReferences()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 34
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4.4661

Importance

Changes 0
Metric Value
cc 4
eloc 12
nc 6
nop 0
dl 0
loc 34
ccs 9
cts 13
cp 0.6923
crap 4.4661
rs 9.8666
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the sweetrdf/InMemoryStoreSqlite package and licensed under
5
 * the terms of the GPL-3 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 bool $refs_deleted;
21
22 11
    public function runQuery($infos)
23
    {
24 11
        $this->infos = $infos;
0 ignored issues
show
Bug Best Practice introduced by
The property infos does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
25
        /* delete */
26 11
        $this->refs_deleted = false;
27
        /* graph(s) only */
28 11
        if (!$this->v('construct_triples', [], $this->infos['query'])) {
29 7
            $tc = $this->deleteTargetGraphs();
30
        }
31
        /* graph(s) + explicit triples */
32 4
        elseif (!$this->v('pattern', [], $this->infos['query'])) {
33 2
            $tc = $this->deleteTriples();
34
        }
35
        /* graph(s) + constructed triples */
36
        else {
37 2
            $tc = $this->deleteConstructedGraph();
38
        }
39
        /* clean up */
40 11
        if ($tc && ($this->refs_deleted || (1 == rand(1, 100)))) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->refs_deleted of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
41 5
            $this->cleanTableReferences();
42
        }
43
44
        return [
45 11
            't_count' => $tc,
46 11
            'delete_time' => 0,
47 11
            'index_update_time' => 0,
48
        ];
49
    }
50
51 7
    private function deleteTargetGraphs()
52
    {
53 7
        $r = 0;
54 7
        foreach ($this->infos['query']['target_graphs'] as $g) {
55 7
            if ($g_id = $this->getTermID($g, 'g')) {
56 5
                $r += $this->store->getDBObject()->exec('DELETE FROM g2t WHERE g = '.$g_id);
57
            }
58
        }
59 7
        $this->refs_deleted = $r ? 1 : 0;
60
61 7
        return $r;
62
    }
63
64 4
    private function deleteTriples()
65
    {
66 4
        $r = 0;
67
        /* graph restriction */
68 4
        $tgs = $this->infos['query']['target_graphs'];
69 4
        $gq = '';
70 4
        foreach ($tgs as $g) {
71 1
            if ($g_id = $this->getTermID($g, 'g')) {
72 1
                $gq .= $gq ? ', '.$g_id : $g_id;
73
            }
74
        }
75 4
        $gq = $gq ? ' AND G.g IN ('.$gq.')' : '';
76
        /* triples */
77 4
        foreach ($this->infos['query']['construct_triples'] as $t) {
78 4
            $q = '';
79 4
            $skip = 0;
80 4
            foreach (['s', 'p', 'o'] as $term) {
81 4
                if (isset($t[$term.'_type']) && preg_match('/(var)/', $t[$term.'_type'])) {
82
                    //$skip = 1;
83
                } else {
84 4
                    $term_id = $this->getTermID($t[$term], $term);
85 4
                    $q .= ($q ? ' AND ' : '').'T.'.$term.'='.$term_id;
86
                    /* explicit lang/dt restricts the matching */
87 4
                    if ('o' == $term) {
88 2
                        $o_lang = $this->v1('o_lang', '', $t);
89 2
                        $o_lang_dt = $this->v1('o_datatype', $o_lang, $t);
90 2
                        if ($o_lang_dt) {
91
                            $q .= ($q ? ' AND ' : '').'T.o_lang_dt='.$this->getTermID($o_lang_dt, 'lang_dt');
92
                        }
93
                    }
94
                }
95
            }
96 4
            if ($skip) {
97
                continue;
98
            }
99 4
            if ($gq) {
100 1
                $sql = 'DELETE FROM g2t WHERE t IN (';
101 1
                $sql .= 'SELECT G.t FROM g2t G JOIN triple T ON T.t = G.t'.$gq.' WHERE '.$q;
102 1
                $sql .= ')';
103
            } else {/* triples only */
104
                // it contains things like "T.s", but we can't use a table alias
105
                // with SQLite when running DELETE queries.
106 3
                $q = str_replace('T.', '', $q);
107 3
                $sql = 'DELETE FROM triple WHERE '.$q;
108
            }
109 4
            $r += $this->store->getDBObject()->exec($sql);
110 4
            if (!empty($this->store->getDBObject()->getErrorMessage())) {
111
                // TODO deletable because never reachable?
112
                throw new Exception($this->store->getDBObject()->getErrorMessage().' in '.$sql);
113
            }
114
        }
115
116 4
        return $r;
117
    }
118
119 2
    private function deleteConstructedGraph()
120
    {
121 2
        $h = new ConstructQueryHandler($this->store);
122 2
        $sub_r = $h->runQuery($this->infos);
123 2
        $triples = $this->getTriplesFromIndex($sub_r);
124 2
        $tgs = $this->infos['query']['target_graphs'];
125 2
        $this->infos = ['query' => ['construct_triples' => $triples, 'target_graphs' => $tgs]];
0 ignored issues
show
Bug Best Practice introduced by
The property infos does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
126
127 2
        return $this->deleteTriples();
128
    }
129
130 2
    private function getTriplesFromIndex(array $index): array
131
    {
132 2
        $r = [];
133 2
        foreach ($index as $s => $ps) {
134 2
            foreach ($ps as $p => $os) {
135 2
                foreach ($os as $o) {
136
                    $r[] = [
137 2
                        's' => $s,
138 2
                        'p' => $p,
139 2
                        'o' => $o['value'],
140 2
                        's_type' => preg_match('/^\_\:/', $s) ? 'bnode' : 'uri',
141 2
                        'o_type' => $o['type'],
142 2
                        'o_datatype' => isset($o['datatype']) ? $o['datatype'] : '',
143 2
                        'o_lang' => isset($o['lang']) ? $o['lang'] : '',
144
                    ];
145
                }
146
            }
147
        }
148
149 2
        return $r;
150
    }
151
152 5
    private function cleanTableReferences()
153
    {
154
        /* check for unconnected triples */
155 5
        $sql = 'SELECT T.t
156
            FROM triple T LEFT JOIN g2t G ON ( G.t = T.t )
157
            WHERE G.t IS NULL
158
            LIMIT 1';
159
160 5
        $numRows = $this->store->getDBObject()->getNumberOfRows($sql);
161
162 5
        if (0 < $numRows) {
163
            /* delete unconnected triples */
164 4
            $sql = 'DELETE FROM triple WHERE t IN (';
165 4
            $sql .= '   SELECT T.t
166
                            FROM triple T
167
                                LEFT JOIN g2t G ON G.t = T.t
168
                            WHERE G.t IS NULL';
169 4
            $sql .= ')';
170 4
            $this->store->getDBObject()->simpleQuery($sql);
171
        }
172
        /* check for unconnected graph refs */
173 5
        if ((1 == rand(1, 10))) {
174
            $sql = '
175
                SELECT G.g FROM g2t G LEFT JOIN triple T ON ( T.t = G.t )
176
                WHERE T.t IS NULL LIMIT 1
177
            ';
178
            if (0 < $this->store->getDBObject()->getNumberOfRows($sql)) {
179
                /* delete unconnected graph refs */
180
                $sql = 'DELETE G
181
                    FROM g2t G
182
                    LEFT JOIN triple T ON (T.t = G.t)
183
                    WHERE T.t IS NULL
184
                ';
185
                $this->store->getDBObject()->simpleQuery($sql);
186
            }
187
        }
188 5
    }
189
}
190