DeleteQueryHandler::deleteConstructedGraph()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 7
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 12
ccs 8
cts 8
cp 1
crap 1
rs 10
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