Skip to content

Commit a58c7cb

Browse files
committed
fixed a PDO error when calling addQuads multiple times
error was: Integrity constraint violation: 19 UNIQUE constraint failed: id2val.id also simplified code a bit: - removed row cache (KeyValueBag) - re-use InsertQueryHandler everytime triples/quads get added (therefore use a global max term ID) - no bulk mode anymore
1 parent 374fd3a commit a58c7cb

File tree

8 files changed

+64
-217
lines changed

8 files changed

+64
-217
lines changed

src/KeyValueBag.php

Lines changed: 0 additions & 47 deletions
This file was deleted.

src/Parser/SPARQLParser.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -835,8 +835,7 @@ protected function xIRI_REF($v)
835835
} elseif ($r = $this->x('\<([^\<\>\s\"\|\^`]*)\>', $v)) {
836836
return [$r[1] ? $r[1] : true, $r[2]];
837837
}
838-
/* allow reserved chars in obvious IRIs */
839-
elseif ($r = $this->x('\<(https?\:[^\s][^\<\>]*)\>', $v)) {
838+
/* allow reserved chars in obvious IRIs */ elseif ($r = $this->x('\<(https?\:[^\s][^\<\>]*)\>', $v)) {
840839
return [$r[1] ? $r[1] : true, $r[2]];
841840
}
842841

src/Store/InMemoryStoreSqlite.php

Lines changed: 15 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
use rdfInterface\NamedNodeInterface;
2121
use rdfInterface\QuadIteratorInterface;
2222
use simpleRdf\DataFactory;
23-
use sweetrdf\InMemoryStoreSqlite\KeyValueBag;
2423
use sweetrdf\InMemoryStoreSqlite\Log\LoggerPool;
2524
use sweetrdf\InMemoryStoreSqlite\NamespaceHelper;
2625
use sweetrdf\InMemoryStoreSqlite\Parser\SPARQLPlusParser;
@@ -35,36 +34,35 @@
3534

3635
class InMemoryStoreSqlite
3736
{
38-
private bool $bulkLoadModeIsActive = true;
39-
40-
private int $bulkLoadModeNextTermId = 1;
41-
4237
private PDOSQLiteAdapter $db;
4338

4439
private DataFactoryInterface $dataFactory;
4540

41+
private InsertQueryHandler $insertQueryHandler;
42+
4643
private LoggerPool $loggerPool;
4744

4845
private NamespaceHelper $namespaceHelper;
4946

50-
private KeyValueBag $rowCache;
51-
5247
private StringReader $stringReader;
5348

49+
/**
50+
* @internal Don't use the constructor directly because parameters may change. Use createInstance() instead.
51+
*/
5452
public function __construct(
5553
PDOSQLiteAdapter $db,
5654
DataFactoryInterface $dataFactory,
5755
NamespaceHelper $namespaceHelper,
5856
LoggerPool $loggerPool,
59-
KeyValueBag $rowCache,
6057
StringReader $stringReader
6158
) {
6259
$this->db = $db;
6360
$this->dataFactory = $dataFactory;
6461
$this->loggerPool = $loggerPool;
6562
$this->namespaceHelper = $namespaceHelper;
66-
$this->rowCache = $rowCache;
6763
$this->stringReader = $stringReader;
64+
65+
$this->insertQueryHandler = new InsertQueryHandler($this, $this->loggerPool->createNewLogger('QueryHandler'));
6866
}
6967

7068
/**
@@ -77,7 +75,6 @@ public static function createInstance()
7775
new DataFactory(),
7876
new NamespaceHelper(),
7977
new LoggerPool(),
80-
new KeyValueBag(),
8178
new StringReader()
8279
);
8380
}
@@ -162,16 +159,7 @@ public function addQuads(iterable | QuadIteratorInterface $quads): void
162159
*/
163160
public function addRawTriples(array $triples, string $graphIri): void
164161
{
165-
$queryHandlerLogger = $this->loggerPool->createNewLogger('QueryHandler');
166-
$queryHandler = new InsertQueryHandler($this, $queryHandlerLogger);
167-
168-
$queryHandler->setRowCache($this->rowCache);
169-
170-
if (true === $this->bulkLoadModeIsActive) {
171-
$queryHandler->activateBulkLoadMode($this->bulkLoadModeNextTermId);
172-
}
173-
174-
$queryHandler->runQuery([
162+
$this->insertQueryHandler->runQuery([
175163
'query' => [
176164
'construct_triples' => $triples,
177165
'target_graph' => $graphIri,
@@ -222,28 +210,18 @@ public function query(string $q, string $format = 'raw'): array | LiteralInterfa
222210
throw new Exception('Inalid query $type given.');
223211
}
224212

225-
$queryHandlerLogger = $this->loggerPool->createNewLogger('QueryHandler');
226-
$queryHandler = new $cls($this, $queryHandlerLogger);
227-
228213
if ('insert' == $queryType) {
229-
$queryHandler->setRowCache($this->rowCache);
230-
231-
if (true === $this->bulkLoadModeIsActive) {
232-
$queryHandler->activateBulkLoadMode($this->bulkLoadModeNextTermId);
233-
}
234-
} elseif ('delete' == $queryType) {
235-
// reset row cache, because it will not be notified of data changes
236-
$this->rowCache->reset();
237-
$this->bulkLoadModeIsActive = false;
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+
$queryHandler = $this->insertQueryHandler;
218+
} else {
219+
$queryHandlerLogger = $this->loggerPool->createNewLogger('QueryHandler');
220+
$queryHandler = new $cls($this, $queryHandlerLogger);
238221
}
239222

240223
$queryResult = $queryHandler->runQuery($infos);
241224

242-
if ('insert' == $queryType && true === $this->bulkLoadModeIsActive) {
243-
// save latest term ID in case further insert into queries follow
244-
$this->bulkLoadModeNextTermId = $queryHandler->getBulkLoadModeNextTermId();
245-
}
246-
247225
$result = null;
248226
if ('raw' == $format) {
249227
// use plain old ARC2 format which is an array of arrays

src/Store/QueryHandler/InsertQueryHandler.php

Lines changed: 10 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -13,31 +13,19 @@
1313

1414
namespace sweetrdf\InMemoryStoreSqlite\Store\QueryHandler;
1515

16-
use sweetrdf\InMemoryStoreSqlite\KeyValueBag;
1716
use sweetrdf\InMemoryStoreSqlite\Log\Logger;
1817
use sweetrdf\InMemoryStoreSqlite\Store\InMemoryStoreSqlite;
1918

2019
use function sweetrdf\InMemoryStoreSqlite\getNormalizedValue;
2120

2221
class InsertQueryHandler extends QueryHandler
2322
{
24-
/**
25-
* If true, store assumes one or more insert into SPARQL queries and will
26-
* skip certain DB operations to speed up insertion process.
27-
*/
28-
private bool $bulkLoadModeIsActive = false;
29-
3023
/**
3124
* Is used if $bulkLoadModeIsActive is true. Determines next term ID for
3225
* entries in id2val, s2val and o2val.
3326
*/
3427
private int $bulkLoadModeNextTermId = 1;
3528

36-
/**
37-
* When set it is used to store term information to speed up insert into operations.
38-
*/
39-
private KeyValueBag $rowCache;
40-
4129
/**
4230
* Is being used for blank nodes to generate a hash which is not only dependent on
4331
* blank node ID and graph, but also on a random value.
@@ -48,25 +36,6 @@ class InsertQueryHandler extends QueryHandler
4836
public function __construct(InMemoryStoreSqlite $store, Logger $logger)
4937
{
5038
parent::__construct($store, $logger);
51-
52-
// default value; will be overridden when running inside InMemoryStoreSqlite
53-
$this->rowCache = new KeyValueBag();
54-
}
55-
56-
public function activateBulkLoadMode(int $bulkLoadModeNextTermId): void
57-
{
58-
$this->bulkLoadModeIsActive = true;
59-
$this->bulkLoadModeNextTermId = $bulkLoadModeNextTermId;
60-
}
61-
62-
public function getBulkLoadModeNextTermId(): int
63-
{
64-
return $this->bulkLoadModeNextTermId;
65-
}
66-
67-
public function setRowCache(KeyValueBag $rowCache): void
68-
{
69-
$this->rowCache = $rowCache;
7039
}
7140

7241
public function runQuery(array $infos)
@@ -100,7 +69,7 @@ private function addTripleToGraph(array $triple, string $graph): void
10069
$graphId = $this->getIdOfExistingTerm($graph, 'id');
10170
if (null == $graphId) {
10271
$graphId = $this->store->getDBObject()->insert('id2val', [
103-
'id' => $this->getMaxTermId(),
72+
'id' => $this->getNextMaxTermId(),
10473
'val' => $graph,
10574
'val_type' => 0, // = uri
10675
]);
@@ -111,7 +80,7 @@ private function addTripleToGraph(array $triple, string $graph): void
11180
*/
11281
$subjectId = $this->getIdOfExistingTerm($triple['s'], 'subject');
11382
if (null == $subjectId) {
114-
$subjectId = $this->getMaxTermId();
83+
$subjectId = $this->getNextMaxTermId();
11584
$this->store->getDBObject()->insert('s2val', [
11685
'id' => $subjectId,
11786
'val' => $triple['s'],
@@ -124,7 +93,7 @@ private function addTripleToGraph(array $triple, string $graph): void
12493
*/
12594
$predicateId = $this->getIdOfExistingTerm($triple['p'], 'id');
12695
if (null == $predicateId) {
127-
$predicateId = $this->getMaxTermId();
96+
$predicateId = $this->getNextMaxTermId();
12897
$this->store->getDBObject()->insert('id2val', [
12998
'id' => $predicateId,
13099
'val' => $triple['p'],
@@ -137,7 +106,7 @@ private function addTripleToGraph(array $triple, string $graph): void
137106
*/
138107
$objectId = $this->getIdOfExistingTerm($triple['o'], 'object');
139108
if (null == $objectId) {
140-
$objectId = $this->getMaxTermId();
109+
$objectId = $this->getNextMaxTermId();
141110
$this->store->getDBObject()->insert('o2val', [
142111
'id' => $objectId,
143112
'val' => $triple['o'],
@@ -163,7 +132,7 @@ private function addTripleToGraph(array $triple, string $graph): void
163132

164133
$oLangDtId = $this->getIdOfExistingTerm($oLangDt, 'id');
165134
if (null == $oLangDtId) {
166-
$oLangDtId = $this->getMaxTermId();
135+
$oLangDtId = $this->getNextMaxTermId();
167136
$this->store->getDBObject()->insert('id2val', [
168137
'id' => $oLangDtId,
169138
'val' => $oLangDt,
@@ -250,32 +219,13 @@ private function prepareTriple(array $triple, string $graph): array
250219
}
251220

252221
/**
253-
* Generates the next valid ID based on latest values in id2val, s2val and o2val.
222+
* Generates the next valid ID.
254223
*
255224
* @return int returns 1 or higher
256225
*/
257-
private function getMaxTermId(): int
226+
private function getNextMaxTermId(): int
258227
{
259-
if (true === $this->bulkLoadModeIsActive) {
260-
return $this->bulkLoadModeNextTermId++;
261-
} else {
262-
$sql = '';
263-
foreach (['id2val', 's2val', 'o2val'] as $table) {
264-
$sql .= !empty($sql) ? ' UNION ' : '';
265-
$sql .= 'SELECT MAX(id) as id FROM '.$table;
266-
}
267-
$result = 0;
268-
269-
$rows = $this->store->getDBObject()->fetchList($sql);
270-
271-
if (\is_array($rows)) {
272-
foreach ($rows as $row) {
273-
$result = ($result < $row['id']) ? $row['id'] : $result;
274-
}
275-
}
276-
277-
return $result + 1;
278-
}
228+
return $this->bulkLoadModeNextTermId++;
279229
}
280230

281231
/**
@@ -290,15 +240,7 @@ private function getIdOfExistingTerm(string $value, string $quadPart): ?int
290240
if ('id' == $quadPart) {
291241
$sql = 'SELECT id, val FROM id2val WHERE val = ?';
292242

293-
$hashKey = md5($sql.json_encode([$value]));
294-
if (false === $this->rowCache->has($hashKey)) {
295-
$row = $this->store->getDBObject()->fetchRow($sql, [$value]);
296-
if (\is_array($row)) {
297-
$this->rowCache->set($hashKey, $row);
298-
}
299-
}
300-
301-
$entry = $this->rowCache->get($hashKey);
243+
$entry = $this->store->getDBObject()->fetchRow($sql, [$value]);
302244

303245
// entry found, use its ID
304246
if (\is_array($entry)) {
@@ -312,15 +254,7 @@ private function getIdOfExistingTerm(string $value, string $quadPart): ?int
312254
$sql = 'SELECT id, val FROM '.$table.' WHERE val_hash = ?';
313255
$params = [$this->getValueHash($value)];
314256

315-
$hashKey = md5($sql.json_encode($params));
316-
if (false === $this->rowCache->has($hashKey)) {
317-
$row = $this->store->getDBObject()->fetchRow($sql, $params);
318-
if (\is_array($row)) {
319-
$this->rowCache->set($hashKey, $row);
320-
}
321-
}
322-
323-
$entry = $this->rowCache->get($hashKey);
257+
$entry = $this->store->getDBObject()->fetchRow($sql, $params);
324258

325259
// entry found, use its ID
326260
if (isset($entry['val']) && $entry['val'] == $value) {

src/Store/QueryHandler/QueryHandler.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,8 @@ public function getTermID($val, $term = '')
6969
}
7070
}
7171
}
72-
}
73-
74-
/* exact match */
75-
else {
72+
} else {
73+
/* exact match */
7674
$sql = 'SELECT id FROM '.$tbl.' WHERE val = ? LIMIT 1';
7775
$row = $this->store->getDBObject()->fetchRow($sql, [$val]);
7876

0 commit comments

Comments
 (0)