DeleteQueryHandler::deleteTriples()   F
last analyzed

Complexity

Conditions 16
Paths 408

Size

Total Lines 53
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 29
CRAP Score 16.2106

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 16
eloc 33
c 1
b 0
f 0
nc 408
nop 0
dl 0
loc 53
ccs 29
cts 32
cp 0.9063
crap 16.2106
rs 2.2222

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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)))) {
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...
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