InMemoryStoreSqlite::query()   C
last analyzed

Complexity

Conditions 14
Paths 25

Size

Total Lines 83
Code Lines 54

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 49
CRAP Score 14.0843

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 14
eloc 54
c 1
b 0
f 0
nc 25
nop 2
dl 0
loc 83
ccs 49
cts 53
cp 0.9245
crap 14.0843
rs 6.2666

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;
15
16
use Exception;
17
use rdfInterface\BlankNodeInterface;
18
use rdfInterface\DataFactoryInterface;
19
use rdfInterface\LiteralInterface;
20
use rdfInterface\NamedNodeInterface;
21
use rdfInterface\QuadIteratorInterface;
22
use simpleRdf\DataFactory;
23
use sweetrdf\InMemoryStoreSqlite\Log\LoggerPool;
24
use sweetrdf\InMemoryStoreSqlite\NamespaceHelper;
25
use sweetrdf\InMemoryStoreSqlite\Parser\SPARQLPlusParser;
26
use sweetrdf\InMemoryStoreSqlite\PDOSQLiteAdapter;
27
use sweetrdf\InMemoryStoreSqlite\Store\QueryHandler\AskQueryHandler;
28
use sweetrdf\InMemoryStoreSqlite\Store\QueryHandler\ConstructQueryHandler;
29
use sweetrdf\InMemoryStoreSqlite\Store\QueryHandler\DeleteQueryHandler;
30
use sweetrdf\InMemoryStoreSqlite\Store\QueryHandler\DescribeQueryHandler;
31
use sweetrdf\InMemoryStoreSqlite\Store\QueryHandler\InsertQueryHandler;
32
use sweetrdf\InMemoryStoreSqlite\Store\QueryHandler\SelectQueryHandler;
33
use sweetrdf\InMemoryStoreSqlite\StringReader;
34
35
class InMemoryStoreSqlite
36
{
37
    private PDOSQLiteAdapter $db;
38
39
    private DataFactoryInterface $dataFactory;
40
41
    private InsertQueryHandler $insertQueryHandler;
42
43
    private LoggerPool $loggerPool;
44
45
    private NamespaceHelper $namespaceHelper;
46
47
    private StringReader $stringReader;
48
49
    /**
50
     * @internal Don't use the constructor directly because parameters may change. Use createInstance() instead.
51
     */
52 100
    public function __construct(
53
        PDOSQLiteAdapter $db,
54
        DataFactoryInterface $dataFactory,
55
        NamespaceHelper $namespaceHelper,
56
        LoggerPool $loggerPool,
57
        StringReader $stringReader
58
    ) {
59 100
        $this->db = $db;
60 100
        $this->dataFactory = $dataFactory;
61 100
        $this->loggerPool = $loggerPool;
62 100
        $this->namespaceHelper = $namespaceHelper;
63 100
        $this->stringReader = $stringReader;
64
65 100
        $this->insertQueryHandler = new InsertQueryHandler($this, $this->loggerPool->createNewLogger('QueryHandler'));
66
    }
67
68
    /**
69
     * Shortcut for people who want a ready-to-use instance.
70
     */
71 100
    public static function createInstance()
72
    {
73 100
        return new self(
74 100
            new PDOSQLiteAdapter(),
75 100
            new DataFactory(),
76 100
            new NamespaceHelper(),
77 100
            new LoggerPool(),
78 100
            new StringReader()
79 100
        );
80
    }
81
82 3
    public function getLoggerPool(): LoggerPool
83
    {
84 3
        return $this->loggerPool;
85
    }
86
87
    public function getNamespaceHelper(): NamespaceHelper
88
    {
89
        return $this->namespaceHelper;
90
    }
91
92 96
    public function getDBObject(): ?PDOSQLiteAdapter
93
    {
94 96
        return $this->db;
95
    }
96
97 1
    public function getDBVersion()
98
    {
99 1
        return $this->db->getServerVersion();
100
    }
101
102 2
    public function addQuads(iterable | QuadIteratorInterface $quads): void
103
    {
104 2
        $triples = [];
105
106 2
        foreach ($quads as $quad) {
107 2
            $graphIri = NamespaceHelper::BASE_NAMESPACE;
108 2
            if (null !== $quad->getGraph()) {
109 2
                $graphIri = $quad->getGraph()->getValue();
110
            }
111
112 2
            if (!isset($triples[$graphIri])) {
113 2
                $triples[$graphIri] = [];
114
            }
115
116 2
            $triple = [
117 2
                's' => $quad->getSubject()->getValue(),
118 2
                's_type' => $quad->getSubject() instanceof NamedNodeInterface ? 'uri' : 'bnode',
119 2
                'p' => $quad->getPredicate()->getValue(),
120 2
                'o' => $quad->getObject()->getValue(),
121 2
                'o_type' => '',
122 2
                'o_lang' => '',
123 2
                'o_datatype' => '',
124 2
            ];
125
126
            // o
127 2
            if ($quad->getObject() instanceof NamedNodeInterface) {
128 2
                $triple['o_type'] = 'uri';
129 2
            } elseif ($quad->getObject() instanceof BlankNodeInterface) {
130
                $triple['o_type'] = 'bnode';
131
            } else {
132 2
                $triple['o_type'] = 'literal';
133 2
                $triple['o_lang'] = $quad->getObject()->getLang();
134 2
                $triple['o_datatype'] = $quad->getObject()->getDatatype();
135
            }
136
137 2
            $triples[$graphIri][] = $triple;
138
        }
139
140 2
        foreach ($triples as $graphIri => $entries) {
141 2
            $this->addRawTriples($entries, $graphIri);
142
        }
143
    }
144
145
    /**
146
     * Adds an array of raw triple-arrays to the store.
147
     *
148
     * Each triple-array in $triples has to look similar to:
149
     *
150
     *      [
151
     *          's' => 'http...',
152
     *          'p' => '...',
153
     *          'o' => '...',
154
     *          's_type' => 'uri',
155
     *          'o_type' => '...',
156
     *          'o_lang' => '...',
157
     *          'o_datatype' => '...',
158
     *      ]
159
     */
160 2
    public function addRawTriples(array $triples, string $graphIri): void
161
    {
162 2
        $this->insertQueryHandler->runQuery([
163 2
            'query' => [
164 2
                'construct_triples' => $triples,
165 2
                'target_graph' => $graphIri,
166 2
            ],
167 2
        ]);
168
    }
169
170
    /**
171
     * Executes a SPARQL query.
172
     *
173
     * @param string $q      SPARQL query
174
     * @param string $format One of: raw, instances
175
     */
176 98
    public function query(string $q, string $format = 'raw'): array | LiteralInterface
177
    {
178 98
        $errors = [];
0 ignored issues
show
Unused Code introduced by
The assignment to $errors is dead and can be removed.
Loading history...
179
180 98
        if (preg_match('/^dump/i', $q)) {
181
            $infos = ['query' => ['type' => 'dump']];
182
        } else {
183 98
            $parserLogger = $this->loggerPool->createNewLogger('SPARQL');
184 98
            $p = new SPARQLPlusParser($parserLogger, $this->namespaceHelper, $this->stringReader);
185 98
            $p->parse($q);
186 98
            $infos = $p->getQueryInfos();
187 98
            $errors = $parserLogger->getEntries('error');
188
189 98
            if (0 < \count($errors)) {
190 6
                throw new Exception('Query failed: '.json_encode($errors));
191
            }
192
        }
193
194 96
        $queryType = $infos['query']['type'];
195 96
        $validTypes = ['select', 'ask', 'describe', 'construct', 'load', 'insert', 'delete', 'dump'];
196 96
        if (!\in_array($queryType, $validTypes)) {
197
            throw new Exception('Unsupported query type "'.$queryType.'"');
198
        }
199
200 96
        $cls = match ($queryType) {
201 3
            'ask' => AskQueryHandler::class,
202 3
            'construct' => ConstructQueryHandler::class,
203 3
            'describe' => DescribeQueryHandler::class,
204 8
            'delete' => DeleteQueryHandler::class,
205 91
            'insert' => InsertQueryHandler::class,
206 86
            'select' => SelectQueryHandler::class,
207 96
        };
208
209 96
        if (empty($cls)) {
210
            throw new Exception('Inalid query $type given.');
211
        }
212
213 96
        if ('insert' == $queryType) {
214
            // reuse InsertQueryHandler because its max term ID
215
            // when it gets recreated everytime the max term ID start by 1 and we get:
216
            // Integrity constraint violation: 19 UNIQUE constraint failed: id2val.id
217 91
            $queryHandler = $this->insertQueryHandler;
218
        } else {
219 92
            $queryHandlerLogger = $this->loggerPool->createNewLogger('QueryHandler');
220 92
            $queryHandler = new $cls($this, $queryHandlerLogger);
221
        }
222
223 96
        $queryResult = $queryHandler->runQuery($infos);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $queryResult is correct as $queryHandler->runQuery($infos) targeting sweetrdf\InMemoryStoreSq...ueryHandler::runQuery() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
224
225 96
        $result = null;
226 96
        if ('raw' == $format) {
227
            // use plain old ARC2 format which is an array of arrays
228 96
            $result = ['query_type' => $queryType, 'result' => $queryResult];
229 2
        } elseif ('instances' == $format) {
230
            // use rdfInstance instance(s) to represent result entries
231 2
            if (\is_array($queryResult)) {
0 ignored issues
show
introduced by
The condition is_array($queryResult) is always false.
Loading history...
232 1
                $variables = $queryResult['variables'];
233
234 1
                foreach ($queryResult['rows'] as $row) {
235 1
                    $resultEntry = [];
236 1
                    foreach ($variables as $variable) {
237 1
                        if ('uri' == $row[$variable.' type']) {
238 1
                            $resultEntry[$variable] = $this->dataFactory->namedNode($row[$variable]);
239 1
                        } elseif ('bnode' == $row[$variable.' type']) {
240 1
                            $resultEntry[$variable] = $this->dataFactory->blankNode($row[$variable]);
241 1
                        } elseif ('literal' == $row[$variable.' type']) {
242 1
                            $resultEntry[$variable] = $this->dataFactory->literal(
243 1
                                $row[$variable],
244 1
                                $row[$variable.' lang'] ?? null,
245 1
                                $row[$variable.' datatype'] ?? null
246 1
                            );
247
                        } else {
248
                            throw new Exception('Invalid type given: '.$row[$variable.' type']);
249
                        }
250
                    }
251 1
                    $result[] = $resultEntry;
252
                }
253
            } else {
254 1
                $result = $this->dataFactory->literal($queryResult);
255
            }
256
        }
257
258 96
        return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result could return the type null which is incompatible with the type-hinted return array|rdfInterface\LiteralInterface. Consider adding an additional type-check to rule them out.
Loading history...
259
    }
260
}
261