Skip to content

Commit a870e0e

Browse files
committed
added backwards compatibility
Signed-off-by: Andy Miller <rhuk@mac.com>
1 parent 94b03d2 commit a870e0e

3 files changed

Lines changed: 45 additions & 16 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## [2.3.1] - 2026-03-09
4+
5+
### Improvements
6+
- **SQLite backward compatibility**: The `RETURNING` clause (requires SQLite 3.35.0+) is now optional. On older SQLite versions (3.24.0+), a fallback `SELECT` query is used to retrieve `doc_id` after upsert operations. This lowers the minimum SQLite requirement from 3.35.0 to 3.24.0 while preserving optimal performance on newer versions.
7+
38
## [2.3.0] - 2026-02-13
49

510
### Security Fixes

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,11 @@ A powerful, pure-PHP search engine library with advanced full-text search capabi
8181

8282
Important: SQLite FTS5 required
8383
- YetiSearch uses SQLite FTS5 virtual tables for full‑text search and BM25 ranking. Your PHP build must link against a SQLite library compiled with FTS5 (ENABLE_FTS5).
84-
- **SQLite 3.35.0 or higher** is required (for `RETURNING` clause support used in document indexing).
84+
- **SQLite 3.24.0 or higher** is required (for `ON CONFLICT` upsert support). SQLite 3.35.0+ is recommended for best performance (uses `RETURNING` clause to avoid extra queries).
8585
- Quick check: `php scripts/check_sqlite_features.php` should report "FTS5: OK" and show the SQLite version. On macOS, Homebrew PHP typically includes a recent SQLite with FTS5; some system PHP builds may not.
8686

8787
- PHP 7.4 or higher
88-
- SQLite 3.35.0 or higher
88+
- SQLite 3.24.0 or higher (3.35.0+ recommended)
8989
- SQLite3 PHP extension
9090
- PDO PHP extension with SQLite driver
9191
- Mbstring PHP extension

src/Storage/SqliteStorage.php

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class SqliteStorage implements StorageInterface
1919
private ?bool $rtreeSupport = null;
2020
private bool $useTermsIndex = false;
2121
private ?bool $hasMathFunctions = null;
22+
private bool $hasReturningSupport = false;
2223
private array $ftsColumnsCache = [];
2324
private array $spatialEnabledCache = [];
2425
private bool $externalContentDefault = false;
@@ -125,6 +126,10 @@ public function connect(array $config): void
125126
// Improve query planning on connect
126127
$this->connection->exec('PRAGMA optimize');
127128

129+
// Detect SQLite version for RETURNING clause support (3.35.0+)
130+
$sqliteVersion = $this->connection->query('SELECT sqlite_version()')->fetchColumn();
131+
$this->hasReturningSupport = version_compare($sqliteVersion, '3.35.0', '>=');
132+
128133
// Detect availability of math functions for Haversine
129134
try {
130135
$this->connection->query('SELECT sin(0.0), cos(0.0), pi()')->fetch();
@@ -398,8 +403,7 @@ public function insert(string $index, array $document): void
398403
$schema = $this->getSchemaMode($index);
399404
$docId = null;
400405
if ($schema === 'external') {
401-
// Use RETURNING to get doc_id directly without extra SELECT query (SQLite 3.35+)
402-
$sql = "
406+
$upsertSql = "
403407
INSERT INTO {$index} (id, content, metadata, language, type, timestamp)
404408
VALUES (?, ?, ?, ?, ?, ?)
405409
ON CONFLICT(id) DO UPDATE SET
@@ -408,12 +412,21 @@ public function insert(string $index, array $document): void
408412
language=excluded.language,
409413
type=excluded.type,
410414
timestamp=excluded.timestamp
411-
RETURNING doc_id
412415
";
413-
$stmt = $this->connection->prepare($sql);
414-
$stmt->execute([$id, $content, $metadata, $language, $type, $timestamp]);
415-
$docId = $stmt->fetchColumn();
416-
$stmt->closeCursor(); // Must close cursor before next operation
416+
if ($this->hasReturningSupport) {
417+
// Use RETURNING to get doc_id directly without extra SELECT query (SQLite 3.35+)
418+
$stmt = $this->connection->prepare($upsertSql . " RETURNING doc_id");
419+
$stmt->execute([$id, $content, $metadata, $language, $type, $timestamp]);
420+
$docId = $stmt->fetchColumn();
421+
$stmt->closeCursor();
422+
} else {
423+
// Fallback for older SQLite: upsert then SELECT doc_id
424+
$stmt = $this->connection->prepare($upsertSql);
425+
$stmt->execute([$id, $content, $metadata, $language, $type, $timestamp]);
426+
$docId = $this->connection->prepare("SELECT doc_id FROM {$index} WHERE id = ?");
427+
$docId->execute([$id]);
428+
$docId = $docId->fetchColumn();
429+
}
417430
} else {
418431
$sql = "
419432
INSERT OR REPLACE INTO {$index}
@@ -493,8 +506,7 @@ public function insertBatch(string $index, array $documents): void
493506
if ($schema === 'external') {
494507
// Use ON CONFLICT DO UPDATE to preserve doc_id on updates.
495508
// INSERT OR REPLACE would delete and re-insert, generating new doc_id.
496-
// Use RETURNING to get the doc_id for FTS/spatial indexing.
497-
$docStmt = $this->connection->prepare("
509+
$upsertSql = "
498510
INSERT INTO {$index} (id, content, metadata, language, type, timestamp)
499511
VALUES (?, ?, ?, ?, ?, ?)
500512
ON CONFLICT(id) DO UPDATE SET
@@ -503,8 +515,15 @@ public function insertBatch(string $index, array $documents): void
503515
language=excluded.language,
504516
type=excluded.type,
505517
timestamp=excluded.timestamp
506-
RETURNING doc_id
507-
");
518+
";
519+
if ($this->hasReturningSupport) {
520+
$upsertSql .= " RETURNING doc_id";
521+
}
522+
$docStmt = $this->connection->prepare($upsertSql);
523+
// Prepare fallback SELECT for older SQLite without RETURNING support
524+
$docIdFallbackStmt = !$this->hasReturningSupport
525+
? $this->connection->prepare("SELECT doc_id FROM {$index} WHERE id = ?")
526+
: null;
508527
} else {
509528
$docStmt = $this->connection->prepare("
510529
INSERT OR REPLACE INTO {$index}
@@ -596,9 +615,14 @@ public function insertBatch(string $index, array $documents): void
596615

597616
// Collect FTS data for batch insert
598617
if ($schema === 'external') {
599-
// Get doc_id from RETURNING clause (works for both inserts and updates)
600-
$docId = $docStmt->fetchColumn();
601-
$docStmt->closeCursor();
618+
// Get doc_id - use RETURNING if available, otherwise fallback SELECT
619+
if ($this->hasReturningSupport) {
620+
$docId = $docStmt->fetchColumn();
621+
$docStmt->closeCursor();
622+
} else {
623+
$docIdFallbackStmt->execute([$id]);
624+
$docId = $docIdFallbackStmt->fetchColumn();
625+
}
602626
$ftsData[] = [$docId, $this->getFieldText($document['content'], 'content', $index)];
603627
} else {
604628
$values = [$id];

0 commit comments

Comments
 (0)